Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
++
++ #include <private/qabstractseries_p.h>
++ #include <private/qabstractaxis_p.h>
++ //themes
++ #include <private/chartthemesystem_p.h>
++ #include <private/chartthemelight_p.h>
++ #include <private/chartthemebluecerulean_p.h>
++ #include <private/chartthemedark_p.h>
++ #include <private/chartthemebrownsand_p.h>
++ #include <private/chartthemebluencs_p.h>
++ #include <private/chartthemehighcontrast_p.h>
++ #include <private/chartthemeblueicy_p.h>
++ #include <private/chartthemeqt_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ ChartThemeManager::ChartThemeManager(QChart* chart) :
++ m_chart(chart)
++ {
++ }
++
++
++ void ChartThemeManager::setTheme(QChart::ChartTheme theme)
++ {
++ if (m_theme.isNull() || theme != m_theme->id()) {
++ switch (theme) {
++ case QChart::ChartThemeLight:
++ m_theme.reset(new ChartThemeLight());
++ break;
++ case QChart::ChartThemeBlueCerulean:
++ m_theme.reset(new ChartThemeBlueCerulean());
++ break;
++ case QChart::ChartThemeDark:
++ m_theme.reset(new ChartThemeDark());
++ break;
++ case QChart::ChartThemeBrownSand:
++ m_theme.reset(new ChartThemeBrownSand());
++ break;
++ case QChart::ChartThemeBlueNcs:
++ m_theme.reset(new ChartThemeBlueNcs());
++ break;
++ case QChart::ChartThemeHighContrast:
++ m_theme.reset(new ChartThemeHighContrast());
++ break;
++ case QChart::ChartThemeBlueIcy:
++ m_theme.reset(new ChartThemeBlueIcy());
++ break;
++ case QChart::ChartThemeQt:
++ m_theme.reset(new ChartThemeQt());
++ break;
++ default:
++ m_theme.reset(new ChartThemeSystem());
++ break;
++ }
++
++ if (!m_theme.isNull()) {
++ decorateChart(m_chart,m_theme.data());
++ decorateLegend(m_chart->legend(),m_theme.data());
++ for (auto axis : std::as_const(m_axisList))
++ axis->d_ptr->initializeTheme(m_theme.data(), true);
++ for (const auto &[series, index] : std::as_const(m_seriesMap).asKeyValueRange())
++ series->d_ptr->initializeTheme(index, m_theme.data(), true);
++ }
++ }
++ }
++
++ // decorateChart is only called when theme is forcibly initialized
++ void ChartThemeManager::decorateChart(QChart *chart, ChartTheme *theme) const
++ {
++ chart->setBackgroundBrush(theme->chartBackgroundGradient());
++
++ QPen pen(Qt::transparent);
++ QBrush brush;
++ chart->setPlotAreaBackgroundBrush(brush);
++ chart->setPlotAreaBackgroundPen(pen);
++ chart->setPlotAreaBackgroundVisible(false);
++
++ chart->setTitleFont(theme->masterFont());
++ chart->setTitleBrush(theme->labelBrush());
++ chart->setDropShadowEnabled(theme->isBackgroundDropShadowEnabled());
++ }
++
++ // decorateLegend is only called when theme is forcibly initialized
++ void ChartThemeManager::decorateLegend(QLegend *legend, ChartTheme *theme) const
++ {
++ legend->setPen(theme->axisLinePen());
++ legend->setBrush(theme->chartBackgroundGradient());
++ legend->setFont(theme->labelFont());
++ legend->setLabelBrush(theme->labelBrush());
++ }
++
++ int ChartThemeManager::createIndexKey(const QList<int> &keys) const
++ {
++ auto keysCopy = keys;
++ std::sort(keysCopy.begin(), keysCopy.end());
++
++ int i = 0;
++ for (const auto key : std::as_const(keysCopy)) {
++ if (i != key)
++ break;
++ ++i;
++ }
++ return i;
++ }
++
++ int ChartThemeManager::seriesCount(QAbstractSeries::SeriesType type) const
++ {
++ int count = 0;
++ QList<QAbstractSeries *> series = m_seriesMap.keys();
++ for (auto s : std::as_const(series)) {
++ if (s->type() == type)
++ count++;
++ }
++ return count;
++ }
++
++ void ChartThemeManager::handleSeriesAdded(QAbstractSeries *series)
++ {
++ int key = createIndexKey(m_seriesMap.values());
++ m_seriesMap.insert(series,key);
++ series->d_ptr->initializeTheme(key,m_theme.data(),false);
++ }
++
++ void ChartThemeManager::handleSeriesRemoved(QAbstractSeries *series)
++ {
++ m_seriesMap.remove(series);
++ }
++
++ void ChartThemeManager::handleAxisAdded(QAbstractAxis *axis)
++ {
++ m_axisList.append(axis);
++ axis->d_ptr->initializeTheme(m_theme.data(),false);
++ }
++
++ void ChartThemeManager::handleAxisRemoved(QAbstractAxis *axis)
++ {
++ m_axisList.removeAll(axis);
++ }
++
++ void ChartThemeManager::updateSeries(QAbstractSeries *series)
++ {
++ if(m_seriesMap.contains(series)){
++ series->d_ptr->initializeTheme(m_seriesMap[series],m_theme.data(),false);
++ }
++ }
++ QList<QGradient> ChartThemeManager::generateSeriesGradients(const QList<QColor>& colors)
++ {
++ QList<QGradient> result;
++ // Generate gradients in HSV color space
++ for (const auto &color : colors) {
++ QLinearGradient g;
++ qreal h = color.hsvHueF();
++ qreal s = color.hsvSaturationF();
++
++ QColor start = color;
++ start.setHsvF(h, 0.0, 1.0);
++ g.setColorAt(0.0, start);
++
++ g.setColorAt(0.5, color);
++
++ QColor end = color;
++ end.setHsvF(h, s, 0.25);
++ g.setColorAt(1.0, end);
++
++ result << g;
++ }
++
++ return result;
++ }
++
++
++ QColor ChartThemeManager::colorAt(const QColor &start, const QColor &end, qreal pos)
++ {
++ Q_ASSERT(pos >= 0.0 && pos <= 1.0);
++ qreal r = start.redF() + ((end.redF() - start.redF()) * pos);
++ qreal g = start.greenF() + ((end.greenF() - start.greenF()) * pos);
++ qreal b = start.blueF() + ((end.blueF() - start.blueF()) * pos);
++ QColor c;
++ c.setRgbF(r, g, b);
++ return c;
++ }
++
++ QColor ChartThemeManager::colorAt(const QGradient &gradient, qreal pos)
++ {
++ Q_ASSERT(pos >= 0 && pos <= 1.0);
++
++ QGradientStops stops = gradient.stops();
++ int count = stops.size();
++
++ // find previous stop relative to position
++ QGradientStop prev = stops.first();
++ for (int i = 0; i < count; i++) {
++ QGradientStop stop = stops.at(i);
++ if (pos > stop.first)
++ prev = stop;
++
++ // given position is actually a stop position?
++ if (pos == stop.first) {
++ //qDebug() << "stop color" << pos;
++ return stop.second;
++ }
++ }
++
++ // find next stop relative to position
++ QGradientStop next = stops.last();
++ for (int i = count - 1; i >= 0; i--) {
++ QGradientStop stop = stops.at(i);
++ if (pos < stop.first)
++ next = stop;
++ }
++
++ //qDebug() << "prev" << prev.first << "pos" << pos << "next" << next.first;
++
++ qreal range = next.first - prev.first;
++ qreal posDelta = pos - prev.first;
++ qreal relativePos = posDelta / range;
++
++ //qDebug() << "range" << range << "posDelta" << posDelta << "relativePos" << relativePos;
++
++ return colorAt(prev.second, next.second, relativePos);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_chartthememanager_p.cpp"