diff --git a/stats/barseries.cpp b/stats/barseries.cpp index 698a87738..2a24c41a5 100644 --- a/stats/barseries.cpp +++ b/stats/barseries.cpp @@ -116,16 +116,17 @@ void BarSeries::BarLabel::setVisible(bool visible) item->setVisible(visible); } -void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount, const QColor &background) +void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount, const QColor &background, const StatsTheme &theme) { // For labels that are on top of a bar, use the corresponding bar color // as background. Rendering on a transparent background gives ugly artifacts. - item->setColor(highlight || isOutside ? darkLabelColor : labelColor(bin_nr, binCount), + item->setColor(highlight || isOutside ? theme.darkLabelColor : theme.labelColor(bin_nr, binCount), isOutside ? Qt::transparent : background); } void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRectF &rect, - int bin_nr, int binCount, const QColor &background) + int bin_nr, int binCount, const QColor &background, + const StatsTheme &theme) { QSizeF itemSize = item->getRect().size(); if (!horizontal) { @@ -173,13 +174,14 @@ void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRe } setVisible(true); // If label changed from inside to outside, or vice-versa, the color might change. - highlight(false, bin_nr, binCount, background); + highlight(false, bin_nr, binCount, background, theme); } BarSeries::Item::Item(BarSeries *series, double lowerBound, double upperBound, std::vector subitemsIn, const QString &binName, const StatsOperationResults &res, int total, - bool horizontal, bool stacked, int binCount) : + bool horizontal, bool stacked, int binCount, + const StatsTheme &theme) : lowerBound(lowerBound), upperBound(upperBound), subitems(std::move(subitemsIn)), @@ -188,30 +190,30 @@ BarSeries::Item::Item(BarSeries *series, double lowerBound, double upperBound, total(total) { for (SubItem &item: subitems) - item.highlight(false, binCount); - updatePosition(series, horizontal, stacked, binCount); + item.highlight(false, binCount, theme); + updatePosition(series, horizontal, stacked, binCount, theme); } -void BarSeries::Item::highlight(int subitem, bool highlight, int binCount) +void BarSeries::Item::highlight(int subitem, bool highlight, int binCount, const StatsTheme &theme) { if (subitem < 0 || subitem >= (int)subitems.size()) return; - subitems[subitem].highlight(highlight, binCount); + subitems[subitem].highlight(highlight, binCount, theme); } // For single-bin charts, selected items are marked with a special fill and border color. // For multi-bin charts, they are marked by a differend border color and border width. -void BarSeries::SubItem::highlight(bool highlight, int binCount) +void BarSeries::SubItem::highlight(bool highlight, int binCount, const StatsTheme &theme) { - fill = highlight ? highlightedColor : binColor(bin_nr, binCount); - QColor border = highlight ? highlightedBorderColor : ::borderColor; + fill = highlight ? theme.highlightedColor : theme.binColor(bin_nr, binCount); + QColor border = highlight ? theme.highlightedBorderColor : theme.borderColor; item->setColor(fill, border); item->setSelected(selected); if (label) - label->highlight(highlight, bin_nr, binCount, fill); + label->highlight(highlight, bin_nr, binCount, fill, theme); } -void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool stacked, int binCount) +void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool stacked, int binCount, const StatsTheme &theme) { if (subitems.empty()) return; @@ -230,7 +232,7 @@ void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool st for (SubItem &item: subitems) { int idx = stacked ? 0 : item.bin_nr; double center = (idx + 0.5) * fullSubWidth + from; - item.updatePosition(series, horizontal, stacked, center - subWidth / 2.0, center + subWidth / 2.0, binCount); + item.updatePosition(series, horizontal, stacked, center - subWidth / 2.0, center + subWidth / 2.0, binCount, theme); } rect = subitems[0].item->getRect(); for (auto it = std::next(subitems.begin()); it != subitems.end(); ++it) @@ -238,7 +240,8 @@ void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool st } void BarSeries::SubItem::updatePosition(BarSeries *series, bool horizontal, bool stacked, - double from, double to, int binCount) + double from, double to, int binCount, + const StatsTheme &theme) { QPointF topLeft, bottomRight; if (horizontal) { @@ -251,7 +254,7 @@ void BarSeries::SubItem::updatePosition(BarSeries *series, bool horizontal, bool QRectF rect(topLeft, bottomRight); item->setRect(rect); if (label) - label->updatePosition(horizontal, stacked, rect, bin_nr, binCount, fill); + label->updatePosition(horizontal, stacked, rect, bin_nr, binCount, fill, theme); } std::vector BarSeries::makeSubItems(std::vector items) const @@ -294,13 +297,13 @@ void BarSeries::add_item(double lowerBound, double upperBound, std::vector= 0 && highlighted.bar < (int)items.size()) { Item &item = items[highlighted.bar]; - item.highlight(index.subitem, true, binCount()); + item.highlight(index.subitem, true, binCount(), theme); if (!information) information = view.createChartItem(); information->setText(makeInfo(item, highlighted.subitem), pos); @@ -422,7 +425,7 @@ bool BarSeries::hover(QPointF pos) void BarSeries::unhighlight() { if (highlighted.bar >= 0 && highlighted.bar < (int)items.size()) - items[highlighted.bar].highlight(highlighted.subitem, false, binCount()); + items[highlighted.bar].highlight(highlighted.subitem, false, binCount(), theme); highlighted = Index(); } @@ -464,7 +467,7 @@ void BarSeries::divesSelected(const QVector &) Index idx(&item - &items[0], &subitem - &item.subitems[0]); bool highlight = idx == highlighted; - item.highlight(idx.subitem, highlight, binCount()); + item.highlight(idx.subitem, highlight, binCount(), theme); } } } diff --git a/stats/barseries.h b/stats/barseries.h index c14ce9ee2..c741c76f6 100644 --- a/stats/barseries.h +++ b/stats/barseries.h @@ -95,8 +95,9 @@ private: bool isOutside; // Is shown outside of bar BarLabel(StatsView &view, const std::vector &labels, int bin_nr, int binCount); void setVisible(bool visible); - void updatePosition(bool horizontal, bool center, const QRectF &rect, int bin_nr, int binCount, const QColor &background); - void highlight(bool highlight, int bin_nr, int binCount, const QColor &background); + void updatePosition(bool horizontal, bool center, const QRectF &rect, int bin_nr, int binCount, + const QColor &background, const StatsTheme &theme); + void highlight(bool highlight, int bin_nr, int binCount, const QColor &background, const StatsTheme &theme); }; struct SubItem { @@ -109,8 +110,8 @@ private: bool selected; QColor fill; void updatePosition(BarSeries *series, bool horizontal, bool stacked, - double from, double to, int binCount); - void highlight(bool highlight, int binCount); + double from, double to, int binCount, const StatsTheme &theme); + void highlight(bool highlight, int binCount, const StatsTheme &theme); }; struct Item { @@ -123,9 +124,11 @@ private: Item(BarSeries *series, double lowerBound, double upperBound, std::vector subitems, const QString &binName, const StatsOperationResults &res, int total, bool horizontal, - bool stacked, int binCount); - void updatePosition(BarSeries *series, bool horizontal, bool stacked, int binCount); - void highlight(int subitem, bool highlight, int binCount); + bool stacked, int binCount, + const StatsTheme &theme); + void updatePosition(BarSeries *series, bool horizontal, bool stacked, int binCount, + const StatsTheme &theme); + void highlight(int subitem, bool highlight, int binCount, const StatsTheme &theme); int getSubItemUnderMouse(const QPointF &f, bool horizontal, bool stacked) const; }; diff --git a/stats/boxseries.cpp b/stats/boxseries.cpp index c1e5537c4..3e752d336 100644 --- a/stats/boxseries.cpp +++ b/stats/boxseries.cpp @@ -27,13 +27,13 @@ BoxSeries::~BoxSeries() } BoxSeries::Item::Item(StatsView &view, BoxSeries *series, double lowerBound, double upperBound, - const StatsQuartiles &qIn, const QString &binName) : + const StatsQuartiles &qIn, const QString &binName, const StatsTheme &theme) : lowerBound(lowerBound), upperBound(upperBound), q(qIn), binName(binName), selected(allDivesSelected(q.dives)) { item = view.createChartItem(ChartZValue::Series, boxBorderWidth); - highlight(false); + highlight(false, theme); updatePosition(series); } @@ -41,14 +41,14 @@ BoxSeries::Item::~Item() { } -void BoxSeries::Item::highlight(bool highlight) +void BoxSeries::Item::highlight(bool highlight, const StatsTheme &theme) { if (highlight) - item->setColor(highlightedColor, highlightedBorderColor); + item->setColor(theme.highlightedColor, theme.highlightedBorderColor); else if (selected) - item->setColor(selectedColor, selectedBorderColor); + item->setColor(theme.selectedColor, theme.selectedBorderColor); else - item->setColor(fillColor, ::borderColor); + item->setColor(theme.fillColor, theme.borderColor); } void BoxSeries::Item::updatePosition(BoxSeries *series) @@ -72,7 +72,7 @@ void BoxSeries::Item::updatePosition(BoxSeries *series) void BoxSeries::append(double lowerBound, double upperBound, const StatsQuartiles &q, const QString &binName) { - items.emplace_back(new Item(view, this, lowerBound, upperBound, q, binName)); + items.emplace_back(new Item(view, this, lowerBound, upperBound, q, binName, theme)); } void BoxSeries::updatePositions() @@ -128,7 +128,7 @@ bool BoxSeries::hover(QPointF pos) // Highlight new item (if any) if (highlighted >= 0 && highlighted < (int)items.size()) { Item &item = *items[highlighted]; - item.highlight(true); + item.highlight(true, theme); if (!information) information = view.createChartItem(); information->setText(formatInformation(item), pos); @@ -142,7 +142,7 @@ bool BoxSeries::hover(QPointF pos) void BoxSeries::unhighlight() { if (highlighted >= 0 && highlighted < (int)items.size()) - items[highlighted]->highlight(false); + items[highlighted]->highlight(false, theme); highlighted = -1; } @@ -183,7 +183,7 @@ void BoxSeries::divesSelected(const QVector &) int idx = &item - &items[0]; bool highlight = idx == highlighted; - item->highlight(highlight); + item->highlight(highlight, theme); } } } diff --git a/stats/boxseries.h b/stats/boxseries.h index f7bf21183..d63d4772d 100644 --- a/stats/boxseries.h +++ b/stats/boxseries.h @@ -40,10 +40,11 @@ private: StatsQuartiles q; QString binName; bool selected; - Item(StatsView &view, BoxSeries *series, double lowerBound, double upperBound, const StatsQuartiles &q, const QString &binName); + Item(StatsView &view, BoxSeries *series, double lowerBound, double upperBound, const StatsQuartiles &q, + const QString &binName, const StatsTheme &theme); ~Item(); void updatePosition(BoxSeries *series); - void highlight(bool highlight); + void highlight(bool highlight, const StatsTheme &theme); }; QString variable, unit; diff --git a/stats/chartitem.cpp b/stats/chartitem.cpp index e3e2aeae4..e7e135d30 100644 --- a/stats/chartitem.cpp +++ b/stats/chartitem.cpp @@ -64,7 +64,7 @@ void ChartPixmapItem::setPositionDirty() markDirty(); } -void ChartPixmapItem::render() +void ChartPixmapItem::render(const StatsTheme &) { if (!node) { createNode(view.w()->createImageNode()); @@ -137,30 +137,25 @@ static QSGTexture *createScatterTexture(StatsView &view, const QColor &color, co return view.w()->createTextureFromImage(img, QQuickWindow::TextureHasAlphaChannel); } -static QSGTexture *scatterItemTexture = nullptr; -static QSGTexture *scatterItemSelectedTexture = nullptr; -static QSGTexture *scatterItemHighlightedTexture = nullptr; -static QSGTexture *selectedTexture = nullptr; // A checkerboard pattern. - -QSGTexture *ChartScatterItem::getTexture() const +QSGTexture *ChartScatterItem::getTexture(const StatsTheme &theme) const { switch (highlight) { default: case Highlight::Unselected: - return scatterItemTexture; + return theme.scatterItemTexture; case Highlight::Selected: - return scatterItemSelectedTexture; + return theme.scatterItemSelectedTexture; case Highlight::Highlighted: - return scatterItemHighlightedTexture; + return theme.scatterItemHighlightedTexture; } } -void ChartScatterItem::render() +void ChartScatterItem::render(const StatsTheme &theme) { - if (!scatterItemTexture) { - scatterItemTexture = register_global(createScatterTexture(view, fillColor, borderColor)); - scatterItemSelectedTexture = register_global(createScatterTexture(view, selectedColor, selectedBorderColor)); - scatterItemHighlightedTexture = register_global(createScatterTexture(view, highlightedColor, highlightedBorderColor)); + if (!theme.scatterItemTexture) { + theme.scatterItemTexture = register_global(createScatterTexture(view, theme.fillColor, theme.borderColor)); + theme.scatterItemSelectedTexture = register_global(createScatterTexture(view, theme.selectedColor, theme.selectedBorderColor)); + theme.scatterItemHighlightedTexture = register_global(createScatterTexture(view, theme.highlightedColor, theme.highlightedBorderColor)); } if (!node) { createNode(view.w()->createImageNode()); @@ -169,7 +164,7 @@ void ChartScatterItem::render() } updateVisible(); if (textureDirty) { - node->node->setTexture(getTexture()); + node->node->setTexture(getTexture(theme)); textureDirty = false; } if (positionDirty) { @@ -288,7 +283,7 @@ ChartPieItem::ChartPieItem(StatsView &v, ChartZValue z, double borderWidth) : Ch { } -static QBrush makeBrush(QColor fill, bool selected) +static QBrush makeBrush(QColor fill, bool selected, const StatsTheme &theme) { if (!selected) return QBrush(fill); @@ -296,18 +291,18 @@ static QBrush makeBrush(QColor fill, bool selected) img.fill(fill); for (int x = 0; x < selectionOverlayPixelSize; ++x) { for (int y = 0; y < selectionOverlayPixelSize; ++y) { - img.setPixelColor(x, y, selectionOverlayColor); + img.setPixelColor(x, y, theme.selectionOverlayColor); img.setPixelColor(x + selectionOverlayPixelSize, y + selectionOverlayPixelSize, - selectionOverlayColor); + theme.selectionOverlayColor); } } return QBrush(img); } -void ChartPieItem::drawSegment(double from, double to, QColor fill, QColor border, bool selected) +void ChartPieItem::drawSegment(double from, double to, QColor fill, QColor border, bool selected, const StatsTheme &theme) { painter->setPen(QPen(border, borderWidth)); - painter->setBrush(makeBrush(fill, selected)); + painter->setBrush(makeBrush(fill, selected, theme)); // For whatever obscure reason, angles of pie pieces are given as 16th of a degree...? // Angles increase CCW, whereas pie charts usually are read CW. Therfore, startAngle // is dervied from "from" and subtracted from the origin angle at 12:00. @@ -353,7 +348,7 @@ void setPoint(QSGGeometry::TexturedPoint2D &v, const QPointF &p, const QPointF & static_cast(t.x()), static_cast(t.y())); } -void ChartLineItem::render() +void ChartLineItem::render(const StatsTheme &) { if (!node) { geometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2)); @@ -384,7 +379,7 @@ void ChartLineItem::render() positionDirty = materialDirty = false; } -void ChartRectLineItem::render() +void ChartRectLineItem::render(const StatsTheme &) { if (!node) { geometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4)); @@ -427,19 +422,19 @@ ChartBarItem::~ChartBarItem() { } -QSGTexture *ChartBarItem::getSelectedTexture() const +QSGTexture *ChartBarItem::getSelectedTexture(const StatsTheme &theme) const { - if (!selectedTexture) { + if (!theme.selectedTexture) { QImage img(2, 2, QImage::Format_ARGB32); img.fill(Qt::transparent); - img.setPixelColor(0, 0, selectionOverlayColor); - img.setPixelColor(1, 1, selectionOverlayColor); - selectedTexture = register_global(view.w()->createTextureFromImage(img, QQuickWindow::TextureHasAlphaChannel)); + img.setPixelColor(0, 0, theme.selectionOverlayColor); + img.setPixelColor(1, 1, theme.selectionOverlayColor); + theme.selectedTexture = register_global(view.w()->createTextureFromImage(img, QQuickWindow::TextureHasAlphaChannel)); } - return selectedTexture; + return theme.selectedTexture; } -void ChartBarItem::render() +void ChartBarItem::render(const StatsTheme &theme) { if (!node) { createNode(view.w()->createRectangleNode()); @@ -483,7 +478,7 @@ void ChartBarItem::render() selectionGeometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)); selectionGeometry->setDrawingMode(QSGGeometry::DrawTriangleFan); selectionMaterial.reset(new QSGTextureMaterial); - selectionMaterial->setTexture(getSelectedTexture()); + selectionMaterial->setTexture(getSelectedTexture(theme)); selectionMaterial->setHorizontalWrapMode(QSGTexture::Repeat); selectionMaterial->setVerticalWrapMode(QSGTexture::Repeat); selectionNode.reset(new QSGGeometryNode); @@ -556,12 +551,12 @@ ChartBoxItem::~ChartBoxItem() { } -void ChartBoxItem::render() +void ChartBoxItem::render(const StatsTheme &theme) { // Remember old dirty values, since ChartBarItem::render() will clear them bool oldPositionDirty = positionDirty; bool oldColorDirty = colorDirty; - ChartBarItem::render(); // This will create the base node, so no need to check for that. + ChartBarItem::render(theme); // This will create the base node, so no need to check for that. if (!whiskersNode) { whiskersGeometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 10)); whiskersGeometry->setDrawingMode(QSGGeometry::DrawLines); diff --git a/stats/chartitem.h b/stats/chartitem.h index 598e854bc..797fba063 100644 --- a/stats/chartitem.h +++ b/stats/chartitem.h @@ -16,12 +16,14 @@ class QSGImageNode; class QSGRectangleNode; class QSGTexture; class QSGTextureMaterial; +class StatsTheme; class StatsView; enum class ChartZValue : int; class ChartItem { public: - virtual void render() = 0; // Only call on render thread! + // Only call on render thread! + virtual void render(const StatsTheme &theme) = 0; bool dirty; // If true, call render() when rebuilding the scene ChartItem *prev, *next; // Double linked list of items const ChartZValue zValue; @@ -58,7 +60,7 @@ public: ~ChartPixmapItem(); void setPos(QPointF pos); - void render() override; // Only call on render thread! + void render(const StatsTheme &theme) override; QRectF getRect() const; protected: void resize(QSizeF size); // Resets the canvas. Attention: image is *unitialized*. @@ -107,7 +109,7 @@ private: class ChartPieItem : public ChartPixmapItem { public: ChartPieItem(StatsView &v, ChartZValue z, double borderWidth); - void drawSegment(double from, double to, QColor fill, QColor border, bool selected); // from and to are relative (0-1 is full disk). + void drawSegment(double from, double to, QColor fill, QColor border, bool selected, const StatsTheme &theme); // from and to are relative (0-1 is full disk). void resize(QSizeF size); // As in base class, but clears the canvas private: double borderWidth; @@ -132,14 +134,14 @@ protected: class ChartLineItem : public ChartLineItemBase { public: using ChartLineItemBase::ChartLineItemBase; - void render() override; // Only call on render thread! + void render(const StatsTheme &theme) override; }; // A simple rectangle without fill. Specified by any two opposing vertices. class ChartRectLineItem : public ChartLineItemBase { public: using ChartLineItemBase::ChartLineItemBase; - void render() override; // Only call on render thread! + void render(const StatsTheme &theme) override; }; // A bar in a bar chart: a rectangle bordered by lines. @@ -151,7 +153,7 @@ public: void setRect(const QRectF &rect); void setSelected(bool selected); QRectF getRect() const; - void render() override; // Only call on render thread! + void render(const StatsTheme &theme) override; protected: QColor color, borderColor; double borderWidth; @@ -168,7 +170,7 @@ private: std::unique_ptr selectionNode; std::unique_ptr selectionMaterial; std::unique_ptr selectionGeometry; - QSGTexture *getSelectedTexture() const; + QSGTexture *getSelectedTexture(const StatsTheme &theme) const; }; // A box-and-whiskers item. This is a bit lazy: derive from the bar item and add whiskers. @@ -178,7 +180,7 @@ public: ~ChartBoxItem(); void setBox(const QRectF &rect, double min, double max, double median); // The rect describes Q1, Q3. QRectF getRect() const; // Note: this extends the center rectangle to include the whiskers. - void render() override; // Only call on render thread! + void render(const StatsTheme &theme) override; private: double min, max, median; std::unique_ptr whiskersNode; @@ -203,12 +205,12 @@ public: }; void setPos(QPointF pos); // Specifies the *center* of the item. void setHighlight(Highlight highlight); // In the future, support different kinds of scatter items. - void render() override; // Only call on render thread! + void render(const StatsTheme &theme) override; QRectF getRect() const; bool contains(QPointF point) const; bool inRect(const QRectF &rect) const; private: - QSGTexture *getTexture() const; + QSGTexture *getTexture(const StatsTheme &theme) const; QRectF rect; QSizeF textureSize; bool positionDirty, textureDirty; diff --git a/stats/informationbox.cpp b/stats/informationbox.cpp index df733f4fc..f65208084 100644 --- a/stats/informationbox.cpp +++ b/stats/informationbox.cpp @@ -11,8 +11,9 @@ static const int distanceFromPointer = 10; // Distance to place box from mouse p InformationBox::InformationBox(StatsView &v) : ChartRectItem(v, ChartZValue::InformationBox, - QPen(informationBorderColor, informationBorder), - QBrush(informationColor), informationBorderRadius), + QPen(v.getCurrentTheme().informationBorderColor, informationBorder), + QBrush(v.getCurrentTheme().informationColor), informationBorderRadius), + theme(v.getCurrentTheme()), width(0.0), height(0.0) { @@ -36,7 +37,7 @@ void InformationBox::setText(const std::vector &text, QPointF pos) ChartRectItem::resize(QSizeF(width, height)); - painter->setPen(QPen(darkLabelColor)); // QPainter uses QPen to set text color! + painter->setPen(QPen(theme.darkLabelColor)); // QPainter uses QPen to set text color! double y = 2.0 * informationBorder; for (size_t i = 0; i < widths.size(); ++i) { QRectF rect(2.0 * informationBorder, y, widths[i], fontHeight); diff --git a/stats/informationbox.h b/stats/informationbox.h index 6ff2bb43e..330fbfd15 100644 --- a/stats/informationbox.h +++ b/stats/informationbox.h @@ -20,6 +20,7 @@ struct InformationBox : ChartRectItem { void setPos(QPointF pos); int recommendedMaxLines() const; private: + const StatsTheme &theme; // Set once in constructor. QFont font; // For future specialization. double width, height; }; diff --git a/stats/legend.cpp b/stats/legend.cpp index fc8656828..b3924264f 100644 --- a/stats/legend.cpp +++ b/stats/legend.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "legend.h" #include "statscolors.h" +#include "statsview.h" #include "zvalues.h" #include @@ -15,8 +16,10 @@ static const double legendInternalBorderSize = 2.0; Legend::Legend(StatsView &view, const std::vector &names) : ChartRectItem(view, ChartZValue::Legend, - QPen(legendBorderColor, legendBorderSize), QBrush(legendColor), legendBoxBorderRadius), + QPen(view.getCurrentTheme().legendBorderColor, legendBorderSize), + QBrush(view.getCurrentTheme().legendColor), legendBoxBorderRadius), displayedItems(0), width(0.0), height(0.0), + theme(view.getCurrentTheme()), font(QFont()), // Make configurable posInitialized(false) { @@ -25,12 +28,12 @@ Legend::Legend(StatsView &view, const std::vector &names) : fontHeight = fm.height(); int idx = 0; for (const QString &name: names) - entries.emplace_back(name, idx++, (int)names.size(), fm); + entries.emplace_back(name, idx++, (int)names.size(), fm, theme); } -Legend::Entry::Entry(const QString &name, int idx, int numBins, const QFontMetrics &fm) : +Legend::Entry::Entry(const QString &name, int idx, int numBins, const QFontMetrics &fm, const StatsTheme &theme) : name(name), - rectBrush(QBrush(binColor(idx, numBins))) + rectBrush(QBrush(theme.binColor(idx, numBins))) { width = fm.height() + 2.0 * legendBoxBorderSize + fm.size(Qt::TextSingleLine, name).width(); } @@ -82,7 +85,7 @@ void Legend::resize() ChartRectItem::resize(QSizeF(width, height)); // Paint rectangles - painter->setPen(QPen(legendBorderColor, legendBoxBorderSize)); + painter->setPen(QPen(theme.legendBorderColor, legendBoxBorderSize)); for (int i = 0; i < displayedItems; ++i) { QPointF itemPos = entries[i].pos; painter->setBrush(entries[i].rectBrush); @@ -94,7 +97,7 @@ void Legend::resize() } // Paint labels - painter->setPen(darkLabelColor); // QPainter uses pen not brush for text! + painter->setPen(theme.darkLabelColor); // QPainter uses pen not brush for text! painter->setFont(font); for (int i = 0; i < displayedItems; ++i) { QPointF itemPos = entries[i].pos; diff --git a/stats/legend.h b/stats/legend.h index fb88920bd..2ba4edaae 100644 --- a/stats/legend.h +++ b/stats/legend.h @@ -10,6 +10,7 @@ #include class QFontMetrics; +class StatsTheme; class Legend : public ChartRectItem { public: @@ -23,11 +24,12 @@ private: QBrush rectBrush; QPointF pos; double width; - Entry(const QString &name, int idx, int numBins, const QFontMetrics &fm); + Entry(const QString &name, int idx, int numBins, const QFontMetrics &fm, const StatsTheme &); }; int displayedItems; double width; double height; + const StatsTheme &theme; // Set once in constructor. QFont font; // The position is specified with respect to the center and in relative terms // with respect to the canvas. diff --git a/stats/pieseries.cpp b/stats/pieseries.cpp index 085b4d28c..de40e56f8 100644 --- a/stats/pieseries.cpp +++ b/stats/pieseries.cpp @@ -18,7 +18,7 @@ static const double innerLabelRadius = 0.75; // 1.0 = at outer border of pie static const double outerLabelRadius = 1.01; // 1.0 = at outer border of pie PieSeries::Item::Item(StatsView &view, const QString &name, int from, std::vector divesIn, int totalCount, - int bin_nr, int numBins) : + int bin_nr, int numBins, const StatsTheme &theme) : name(name), dives(std::move(divesIn)), selected(allDivesSelected(dives)) @@ -43,7 +43,7 @@ PieSeries::Item::Item(StatsView &view, const QString &name, int from, std::vecto innerLabel = view.createChartItem(ChartZValue::SeriesLabels, f, innerLabelText); outerLabel = view.createChartItem(ChartZValue::SeriesLabels, f, name); - outerLabel->setColor(darkLabelColor); + outerLabel->setColor(theme.darkLabelColor); } void PieSeries::Item::updatePositions(const QPointF ¢er, double radius) @@ -72,13 +72,13 @@ void PieSeries::Item::updatePositions(const QPointF ¢er, double radius) } } -void PieSeries::Item::highlight(ChartPieItem &item, int bin_nr, bool highlight, int numBins) +void PieSeries::Item::highlight(ChartPieItem &item, int bin_nr, bool highlight, int numBins, const StatsTheme &theme) { - QColor fill = highlight ? highlightedColor : binColor(bin_nr, numBins); - QColor border = highlight ? highlightedBorderColor : ::borderColor; + QColor fill = highlight ? theme.highlightedColor : theme.binColor(bin_nr, numBins); + QColor border = highlight ? theme.highlightedBorderColor : theme.borderColor; if (innerLabel) - innerLabel->setColor(highlight ? darkLabelColor : labelColor(bin_nr, numBins), fill); - item.drawSegment(angleFrom, angleTo, fill, border, selected); + innerLabel->setColor(highlight ? theme.darkLabelColor : theme.labelColor(bin_nr, numBins), fill); + item.drawSegment(angleFrom, angleTo, fill, border, selected, theme); } PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName, @@ -147,7 +147,7 @@ PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const for (auto it2 = sorted.begin(); it2 != it; ++it2) { int count = (int)data[*it2].second.size(); items.emplace_back(view, data[*it2].first, act, std::move(data[*it2].second), - totalCount, (int)items.size(), numBins); + totalCount, (int)items.size(), numBins, theme); act += count; } @@ -162,7 +162,7 @@ PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const otherDives.push_back(d); } QString name = StatsTranslations::tr("other (%1 items)").arg(other.size()); - items.emplace_back(view, name, act, std::move(otherDives), totalCount, (int)items.size(), numBins); + items.emplace_back(view, name, act, std::move(otherDives), totalCount, (int)items.size(), numBins, theme); } } @@ -181,7 +181,7 @@ void PieSeries::updatePositions() int i = 0; for (Item &segment: items) { segment.updatePositions(center, radius); - segment.highlight(*item, i, i == highlighted, (int)items.size()); // Draw segment + segment.highlight(*item, i, i == highlighted, (int)items.size(), theme); // Draw segment ++i; } } @@ -255,7 +255,7 @@ bool PieSeries::hover(QPointF pos) // Highlight new item (if any) if (highlighted >= 0 && highlighted < (int)items.size()) { - items[highlighted].highlight(*item, highlighted, true, (int)items.size()); + items[highlighted].highlight(*item, highlighted, true, (int)items.size(), theme); if (!information) information = view.createChartItem(); information->setText(makeInfo(highlighted), pos); @@ -269,7 +269,7 @@ bool PieSeries::hover(QPointF pos) void PieSeries::unhighlight() { if (highlighted >= 0 && highlighted < (int)items.size()) - items[highlighted].highlight(*item, highlighted, false, (int)items.size()); + items[highlighted].highlight(*item, highlighted, false, (int)items.size(), theme); highlighted = -1; } @@ -314,7 +314,7 @@ void PieSeries::divesSelected(const QVector &) segment.selected = selected; int idx = &segment - &items[0]; - segment.highlight(*item, idx, idx == highlighted, (int)items.size()); + segment.highlight(*item, idx, idx == highlighted, (int)items.size(), theme); } } } diff --git a/stats/pieseries.h b/stats/pieseries.h index 95adf32c0..f696006cb 100644 --- a/stats/pieseries.h +++ b/stats/pieseries.h @@ -49,9 +49,9 @@ private: QPointF innerLabelPos, outerLabelPos; // With respect to a (-1, -1)-(1, 1) rectangle. bool selected; Item(StatsView &view, const QString &name, int from, std::vector dives, int totalCount, - int bin_nr, int numBins); + int bin_nr, int numBins, const StatsTheme &theme); void updatePositions(const QPointF ¢er, double radius); - void highlight(ChartPieItem &item, int bin_nr, bool highlight, int numBins); + void highlight(ChartPieItem &item, int bin_nr, bool highlight, int numBins, const StatsTheme &theme); }; std::vector items; int totalCount; diff --git a/stats/quartilemarker.cpp b/stats/quartilemarker.cpp index 611737d76..967144dfd 100644 --- a/stats/quartilemarker.cpp +++ b/stats/quartilemarker.cpp @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 #include "quartilemarker.h" -#include "statscolors.h" #include "statsaxis.h" +#include "statscolors.h" +#include "statsview.h" #include "zvalues.h" static const double quartileMarkerSize = 15.0; QuartileMarker::QuartileMarker(StatsView &view, double pos, double value, StatsAxis *xAxis, StatsAxis *yAxis) : - ChartLineItem(view, ChartZValue::ChartFeatures, quartileMarkerColor, 2.0), + ChartLineItem(view, ChartZValue::ChartFeatures, view.getCurrentTheme().quartileMarkerColor, 2.0), xAxis(xAxis), yAxis(yAxis), pos(pos), value(value) diff --git a/stats/regressionitem.cpp b/stats/regressionitem.cpp index 3377ec419..1bdabeb54 100644 --- a/stats/regressionitem.cpp +++ b/stats/regressionitem.cpp @@ -2,6 +2,7 @@ #include "regressionitem.h" #include "statsaxis.h" #include "statscolors.h" +#include "statsview.h" #include "zvalues.h" #include @@ -11,6 +12,7 @@ static const double regressionLineWidth = 2.0; RegressionItem::RegressionItem(StatsView &view, regression_data reg, StatsAxis *xAxis, StatsAxis *yAxis) : ChartPixmapItem(view, ChartZValue::ChartFeatures), + theme(view.getCurrentTheme()), xAxis(xAxis), yAxis(yAxis), reg(reg), regression(true), confidence(true) { @@ -86,7 +88,7 @@ void RegressionItem::updatePosition() img->fill(Qt::transparent); if (confidence) { - QColor col(regressionItemColor); + QColor col(theme.regressionItemColor); col.setAlphaF((float)reg.r2); painter->setPen(Qt::NoPen); painter->setBrush(QBrush(col)); @@ -94,7 +96,7 @@ void RegressionItem::updatePosition() } if (regression) { - painter->setPen(QPen(regressionItemColor, regressionLineWidth)); + painter->setPen(QPen(theme.regressionItemColor, regressionLineWidth)); painter->drawLine(QPointF(linePolygon[0]), QPointF(linePolygon[1])); } diff --git a/stats/regressionitem.h b/stats/regressionitem.h index 24141122c..3e5edca03 100644 --- a/stats/regressionitem.h +++ b/stats/regressionitem.h @@ -5,6 +5,7 @@ #include "chartitem.h" class StatsAxis; +class StatsTheme; class StatsView; struct regression_data { @@ -20,6 +21,7 @@ public: void updatePosition(); void setFeatures(bool regression, bool confidence); private: + const StatsTheme &theme; // Initialized once in constructor StatsAxis *xAxis, *yAxis; regression_data reg; bool regression, confidence; diff --git a/stats/statsaxis.cpp b/stats/statsaxis.cpp index 92c972e0e..7f9f5a06d 100644 --- a/stats/statsaxis.cpp +++ b/stats/statsaxis.cpp @@ -26,7 +26,8 @@ static const double axisTitleSpaceVertical = 2.0; // Space between labels and ti StatsAxis::StatsAxis(StatsView &view, const QString &title, bool horizontal, bool labelsBetweenTicks) : ChartPixmapItem(view, ChartZValue::Axes), - line(view.createChartItem(ChartZValue::Axes, axisColor, axisWidth)), + theme(view.getCurrentTheme()), + line(view.createChartItem(ChartZValue::Axes, theme.axisColor, axisWidth)), title(title), horizontal(horizontal), labelsBetweenTicks(labelsBetweenTicks), size(1.0), zeroOnScreen(0.0), min(0.0), max(1.0), labelWidth(0.0) { @@ -128,7 +129,7 @@ void StatsAxis::addLabel(const QFontMetrics &fm, const QString &label, double po void StatsAxis::addTick(double pos) { - ticks.push_back({ view.createChartItem(ChartZValue::Axes, axisColor, axisTickWidth), pos }); + ticks.push_back({ view.createChartItem(ChartZValue::Axes, theme.axisColor, axisTickWidth), pos }); } std::vector StatsAxis::ticksPositions() const @@ -190,7 +191,7 @@ void StatsAxis::setSize(double sizeIn) offset = QPointF(round(offsetX), round(offsetY)); img->fill(Qt::transparent); - painter->setPen(QPen(darkLabelColor)); + painter->setPen(QPen(theme.darkLabelColor)); painter->setFont(labelFont); for (const Label &label: labels) { double x = (label.pos - min) / (max - min) * size + offset.x() - round(label.width / 2.0); @@ -219,7 +220,7 @@ void StatsAxis::setSize(double sizeIn) offset = QPointF(round(offsetX), round(offsetY)); img->fill(Qt::transparent); - painter->setPen(QPen(darkLabelColor)); + painter->setPen(QPen(theme.darkLabelColor)); painter->setFont(labelFont); for (const Label &label: labels) { double y = (min - label.pos) / (max - min) * size + offset.y() - round(fontHeight / 2.0); diff --git a/stats/statsaxis.h b/stats/statsaxis.h index 02662ddd9..0cd6d668b 100644 --- a/stats/statsaxis.h +++ b/stats/statsaxis.h @@ -37,6 +37,7 @@ public: protected: StatsAxis(StatsView &view, const QString &title, bool horizontal, bool labelsBetweenTicks); + const StatsTheme &theme; // Initialized once in constructor. ChartItemPtr line; QString title; double titleWidth; diff --git a/stats/statscolors.cpp b/stats/statscolors.cpp index 7930b2a5f..c59988fa4 100644 --- a/stats/statscolors.cpp +++ b/stats/statscolors.cpp @@ -1,4 +1,5 @@ #include "statscolors.h" +#include "statstranslations.h" // Colors created using the Chroma.js Color Palette Helper // https://vis4.net/palettes/#/50|d|00108c,3ed8ff,ffffe0|ffffe0,ff005e,743535|1|1 @@ -15,22 +16,65 @@ static const QColor binColors[] = { QRgb(0xa83e49), QRgb(0x9d3a44), QRgb(0x90383f), QRgb(0x83363a), QRgb(0x743535) }; -// Pick roughly equidistant colors out of the color set above -// if we need more bins than we have colors (what chart is THAT?) simply loop -QColor binColor(int bin, int numBins) +StatsTheme::StatsTheme() : + scatterItemTexture(nullptr), + scatterItemSelectedTexture(nullptr), + scatterItemHighlightedTexture(nullptr), + selectedTexture(nullptr) { - if (numBins == 1 || bin < 0 || bin >= numBins) - return fillColor; - if (numBins > (int)std::size(binColors)) - return binColors[bin % std::size(binColors)]; - - // use integer math to spread out the indices - int idx = bin * (std::size(binColors) - 1) / (numBins - 1); - return binColors[idx]; } -// Figure out if we want a light or a dark label -QColor labelColor(int bin, size_t numBins) -{ - return (binColor(bin, numBins).lightness() < 150) ? lightLabelColor : darkLabelColor; -} +class StatsThemeLight : public StatsTheme { +public: + StatsThemeLight() + { + backgroundColor = Qt::white; + fillColor = QColor(0x44, 0x76, 0xaa); + borderColor = QColor(0x66, 0xb2, 0xff); + selectedColor = QColor(0xaa, 0x76, 0x44); + selectedBorderColor = QColor(0xff, 0xb2, 0x66); + highlightedColor = Qt::yellow; + highlightedBorderColor = QColor(0xaa, 0xaa, 0x22); + darkLabelColor = Qt::black; + lightLabelColor = Qt::white; + axisColor = Qt::black; + gridColor = QColor(0xcc, 0xcc, 0xcc); + informationBorderColor = Qt::black; + informationColor = QColor(0xff, 0xff, 0x00, 192); // Note: fourth argument is opacity + legendColor = QColor(0x00, 0x8e, 0xcc, 192); // Note: fourth argument is opacity + legendBorderColor = Qt::black; + quartileMarkerColor = Qt::red; + regressionItemColor = Qt::red; + meanMarkerColor = Qt::green; + medianMarkerColor = Qt::red; + selectionLassoColor = Qt::black; + selectionOverlayColor = Qt::lightGray; + } +private: + QString name() const + { + return StatsTranslations::tr("Light"); + } + + // Pick roughly equidistant colors out of the color set above + // if we need more bins than we have colors (what chart is THAT?) simply loop + QColor binColor(int bin, int numBins) const override + { + if (numBins == 1 || bin < 0 || bin >= numBins) + return fillColor; + if (numBins > (int)std::size(binColors)) + return binColors[bin % std::size(binColors)]; + + // use integer math to spread out the indices + int idx = bin * (std::size(binColors) - 1) / (numBins - 1); + return binColors[idx]; + } + + QColor labelColor(int bin, size_t numBins) const override + { + return (binColor(bin, numBins).lightness() < 150) ? lightLabelColor : darkLabelColor; + } +}; + +static StatsThemeLight statsThemeLight; +std::vector statsThemes = { &statsThemeLight }; diff --git a/stats/statscolors.h b/stats/statscolors.h index 47344b082..e7f6c0886 100644 --- a/stats/statscolors.h +++ b/stats/statscolors.h @@ -3,31 +3,53 @@ #ifndef STATSCOLORS_H #define STATSCOLORS_H +#include #include +#include -inline const QColor backgroundColor(Qt::white); -inline const QColor fillColor(0x44, 0x76, 0xaa); -inline const QColor borderColor(0x66, 0xb2, 0xff); -inline const QColor selectedColor(0xaa, 0x76, 0x44); -inline const QColor selectedBorderColor(0xff, 0xb2, 0x66); -inline const QColor highlightedColor(Qt::yellow); -inline const QColor highlightedBorderColor(0xaa, 0xaa, 0x22); -inline const QColor darkLabelColor(Qt::black); -inline const QColor lightLabelColor(Qt::white); -inline const QColor axisColor(Qt::black); -inline const QColor gridColor(0xcc, 0xcc, 0xcc); -inline const QColor informationBorderColor(Qt::black); -inline const QColor informationColor(0xff, 0xff, 0x00, 192); // Note: fourth argument is opacity -inline const QColor legendColor(0x00, 0x8e, 0xcc, 192); // Note: fourth argument is opacity -inline const QColor legendBorderColor(Qt::black); -inline const QColor quartileMarkerColor(Qt::red); -inline const QColor regressionItemColor(Qt::red); -inline const QColor meanMarkerColor(Qt::green); -inline const QColor medianMarkerColor(Qt::red); -inline const QColor selectionLassoColor(Qt::black); -inline const QColor selectionOverlayColor(Qt::lightGray); +class QSGTexture; -QColor binColor(int bin, int numBins); -QColor labelColor(int bin, size_t numBins); +class StatsTheme { +public: + StatsTheme(); + virtual QString name() const = 0; + QColor backgroundColor; + QColor fillColor; + QColor borderColor; + QColor selectedColor; + QColor selectedBorderColor; + QColor highlightedColor; + QColor highlightedBorderColor; + // The dark and light label colors are with respect to the light theme. + // In the dark theme, dark is light and light is dark. Might want to change the names! + QColor darkLabelColor; + QColor lightLabelColor; + QColor axisColor; + QColor gridColor; + QColor informationBorderColor; + QColor informationColor; + QColor legendColor; + QColor legendBorderColor; + QColor quartileMarkerColor; + QColor regressionItemColor; + QColor meanMarkerColor; + QColor medianMarkerColor; + QColor selectionLassoColor; + QColor selectionOverlayColor; + virtual QColor binColor(int bin, int numBins) const = 0; + virtual QColor labelColor(int bin, size_t numBins) const = 0; + + // These are cashes for the QSG rendering engine + // Note: Originally these were std::unique_ptrs, which automatically + // freed the textures on exit. However, destroying textures after + // QApplication finished its thread leads to crashes. Therefore, these + // are now normal pointers and the texture objects are leaked. + mutable QSGTexture *scatterItemTexture = nullptr; + mutable QSGTexture *scatterItemSelectedTexture = nullptr; + mutable QSGTexture *scatterItemHighlightedTexture = nullptr; + mutable QSGTexture *selectedTexture = nullptr; // A checkerboard pattern. +}; + +extern std::vector statsThemes; #endif diff --git a/stats/statsgrid.cpp b/stats/statsgrid.cpp index f29069341..48b21fd41 100644 --- a/stats/statsgrid.cpp +++ b/stats/statsgrid.cpp @@ -9,7 +9,7 @@ static const double gridWidth = 1.0; StatsGrid::StatsGrid(StatsView &view, const StatsAxis &xAxis, const StatsAxis &yAxis) - : view(view), xAxis(xAxis), yAxis(yAxis) + : view(view), theme(view.getCurrentTheme()), xAxis(xAxis), yAxis(yAxis) { } @@ -28,11 +28,11 @@ void StatsGrid::updatePositions() return; for (double x: xtics) { - lines.push_back(view.createChartItem(ChartZValue::Grid, gridColor, gridWidth)); + lines.push_back(view.createChartItem(ChartZValue::Grid, theme.gridColor, gridWidth)); lines.back()->setLine(QPointF(x, ytics.front()), QPointF(x, ytics.back())); } for (double y: ytics) { - lines.push_back(view.createChartItem(ChartZValue::Grid, gridColor, gridWidth)); + lines.push_back(view.createChartItem(ChartZValue::Grid, theme.gridColor, gridWidth)); lines.back()->setLine(QPointF(xtics.front(), y), QPointF(xtics.back(), y)); } } diff --git a/stats/statsgrid.h b/stats/statsgrid.h index 696341c0b..80ab53620 100644 --- a/stats/statsgrid.h +++ b/stats/statsgrid.h @@ -7,6 +7,7 @@ #include class StatsAxis; +class StatsTheme; class StatsView; class ChartLineItem; @@ -16,6 +17,7 @@ public: void updatePositions(); private: StatsView &view; + const StatsTheme &theme; // Initialized once in constructor. const StatsAxis &xAxis, &yAxis; std::vector> lines; }; diff --git a/stats/statsseries.cpp b/stats/statsseries.cpp index 08d0c031d..6c3a5f1b0 100644 --- a/stats/statsseries.cpp +++ b/stats/statsseries.cpp @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 #include "statsseries.h" #include "statsaxis.h" +#include "statsview.h" StatsSeries::StatsSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis) : - view(view), xAxis(xAxis), yAxis(yAxis) + view(view), theme(view.getCurrentTheme()), xAxis(xAxis), yAxis(yAxis) { } diff --git a/stats/statsseries.h b/stats/statsseries.h index 16c88b047..2f995a05e 100644 --- a/stats/statsseries.h +++ b/stats/statsseries.h @@ -10,6 +10,7 @@ #include class StatsAxis; +class StatsTheme; class StatsView; struct dive; class QRectF; @@ -30,6 +31,7 @@ public: protected: StatsView &view; + const StatsTheme &theme; // Theme is only set once in the constructor StatsAxis *xAxis, *yAxis; // May be zero for charts without axes (pie charts). QPointF toScreen(QPointF p); }; diff --git a/stats/statsview.cpp b/stats/statsview.cpp index cd1cc7591..a0e059f88 100644 --- a/stats/statsview.cpp +++ b/stats/statsview.cpp @@ -35,6 +35,7 @@ static const double selectionLassoWidth = 2.0; // Border between title and char StatsView::StatsView(QQuickItem *parent) : QQuickItem(parent), backgroundDirty(true), + currentTheme(statsThemes[0]), highlightedSeries(nullptr), xAxis(nullptr), yAxis(nullptr), @@ -99,7 +100,7 @@ void StatsView::mousePressEvent(QMouseEvent *event) if (selectionRect) deleteChartItem(selectionRect); // Ooops. Already a selection in place. dragStartMouse = pos; - selectionRect = createChartItem(ChartZValue::Selection, selectionLassoColor, selectionLassoWidth); + selectionRect = createChartItem(ChartZValue::Selection, currentTheme->selectionLassoColor, selectionLassoWidth); selectionModifier = modifier; oldSelection = modifier.ctrl ? getDiveSelection() : std::vector(); grabMouse(); @@ -143,7 +144,7 @@ RootNode::RootNode(StatsView &view) : view(view) // also be done on the widget level, but would have to be done // separately for desktop and mobile, so do it here. backgroundNode.reset(view.w()->createRectangleNode()); - backgroundNode->setColor(backgroundColor); + backgroundNode->setColor(view.getCurrentTheme().backgroundColor); appendChildNode(backgroundNode.get()); for (auto &zNode: zNodes) { @@ -184,7 +185,7 @@ QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNod } for (ChartItem *item = dirtyItems.first; item; item = item->next) { - item->render(); + item->render(*currentTheme); item->dirty = false; } dirtyItems.splice(cleanItems); @@ -299,6 +300,18 @@ QQuickWindow *StatsView::w() const return window(); } +void StatsView::setTheme(int idx) +{ + idx = std::clamp(idx, 0, (int)statsThemes.size() - 1); + currentTheme = statsThemes[idx]; + rootNode->backgroundNode->setColor(currentTheme->backgroundColor); +} + +const StatsTheme &StatsView::getCurrentTheme() const +{ + return *currentTheme; +} + QSizeF StatsView::size() const { return boundingRect().size(); @@ -459,7 +472,7 @@ void StatsView::setTitle(const QString &s) return; } title = createChartItem(ChartZValue::Legend, titleFont, s); - title->setColor(darkLabelColor); + title->setColor(currentTheme->darkLabelColor); } void StatsView::updateTitlePos() @@ -1128,10 +1141,10 @@ void StatsView::plotHistogramCountChart(const std::vector &dives, if (categoryVariable->type() == StatsVariable::Type::Numeric) { double mean = categoryVariable->mean(dives); if (!std::isnan(mean)) - meanMarker = createChartItem(mean, isHorizontal, meanMarkerColor, xAxis, yAxis); + meanMarker = createChartItem(mean, isHorizontal, currentTheme->meanMarkerColor, xAxis, yAxis); double median = categoryVariable->quartiles(dives).q2; if (!std::isnan(median)) - medianMarker = createChartItem(median, isHorizontal, medianMarkerColor, xAxis, yAxis); + medianMarker = createChartItem(median, isHorizontal, currentTheme->medianMarkerColor, xAxis, yAxis); } } diff --git a/stats/statsview.h b/stats/statsview.h index 938e81e29..249520481 100644 --- a/stats/statsview.h +++ b/stats/statsview.h @@ -29,6 +29,7 @@ class QuartileMarker; class RegressionItem; class StatsAxis; class StatsGrid; +class StatsTheme; class Legend; class QSGTexture; class RootNode; // Internal implementation detail @@ -53,6 +54,8 @@ public: QQuickWindow *w() const; // Make window available to items QSizeF size() const; QRectF plotArea() const; + void setTheme(int idx); // Invalid indexes will result in the default theme. Chart must be replot for theme to become effective. + const StatsTheme &getCurrentTheme() const; void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread! void registerChartItem(ChartItem &item); void registerDirtyChartItem(ChartItem &item); @@ -140,6 +143,7 @@ private: void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal); StatsState state; + const StatsTheme *currentTheme; QFont titleFont; std::vector> series; std::unique_ptr grid;