statistics: collect colors in a StatsTheme class

To enable rudimentary theming, collect all colors in a new
theme class. The class has to be passed down to the various
items.

In general the items save a reference to the them in the
constructor. Alternatively, they might also just query
the StatsView everytime they need to access a color.
For now, it's hard the say what is preferred: a reference
per item or a function call per invokation?

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2021-02-16 17:05:39 +01:00 committed by Dirk Hohndel
parent 56e02dbcc0
commit b5aac29cea
25 changed files with 272 additions and 166 deletions

View file

@ -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<SubItem> 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::SubItem> BarSeries::makeSubItems(std::vector<SubItemDesc> items) const
@ -294,13 +297,13 @@ void BarSeries::add_item(double lowerBound, double upperBound, std::vector<SubIt
if (subitems.empty())
return;
items.emplace_back(this, lowerBound, upperBound, std::move(subitems), binName, res,
total, horizontal, stacked, binCount());
total, horizontal, stacked, binCount(), theme);
}
void BarSeries::updatePositions()
{
for (Item &item: items)
item.updatePosition(this, horizontal, stacked, binCount());
item.updatePosition(this, horizontal, stacked, binCount(), theme);
}
// Attention: this supposes that items are sorted by position and no bar is inside another bar!
@ -407,7 +410,7 @@ bool BarSeries::hover(QPointF pos)
// Highlight new item (if any)
if (highlighted.bar >= 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<InformationBox>();
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<dive *> &)
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);
}
}
}

View file

@ -95,8 +95,9 @@ private:
bool isOutside; // Is shown outside of bar
BarLabel(StatsView &view, const std::vector<QString> &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<SubItem> 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;
};

View file

@ -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<ChartBoxItem>(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<InformationBox>();
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<dive *> &)
int idx = &item - &items[0];
bool highlight = idx == highlighted;
item->highlight(highlight);
item->highlight(highlight, theme);
}
}
}

View file

@ -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;

View file

@ -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<float>(t.x()), static_cast<float>(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);

View file

@ -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<QSGGeometryNode> selectionNode;
std::unique_ptr<QSGTextureMaterial> selectionMaterial;
std::unique_ptr<QSGGeometry> 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<QSGGeometryNode> 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;

View file

@ -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<QString> &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);

View file

@ -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;
};

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "legend.h"
#include "statscolors.h"
#include "statsview.h"
#include "zvalues.h"
#include <cmath>
@ -15,8 +16,10 @@ static const double legendInternalBorderSize = 2.0;
Legend::Legend(StatsView &view, const std::vector<QString> &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<QString> &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;

View file

@ -10,6 +10,7 @@
#include <QFont>
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.

View file

@ -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<dive *> 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<ChartTextItem>(ChartZValue::SeriesLabels, f, innerLabelText);
outerLabel = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, name);
outerLabel->setColor(darkLabelColor);
outerLabel->setColor(theme.darkLabelColor);
}
void PieSeries::Item::updatePositions(const QPointF &center, double radius)
@ -72,13 +72,13 @@ void PieSeries::Item::updatePositions(const QPointF &center, 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<InformationBox>();
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<dive *> &)
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);
}
}
}

View file

@ -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<dive *> dives, int totalCount,
int bin_nr, int numBins);
int bin_nr, int numBins, const StatsTheme &theme);
void updatePositions(const QPointF &center, 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<Item> items;
int totalCount;

View file

@ -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)

View file

@ -2,6 +2,7 @@
#include "regressionitem.h"
#include "statsaxis.h"
#include "statscolors.h"
#include "statsview.h"
#include "zvalues.h"
#include <cmath>
@ -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]));
}

View file

@ -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;

View file

@ -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<ChartLineItem>(ChartZValue::Axes, axisColor, axisWidth)),
theme(view.getCurrentTheme()),
line(view.createChartItem<ChartLineItem>(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<ChartLineItem>(ChartZValue::Axes, axisColor, axisTickWidth), pos });
ticks.push_back({ view.createChartItem<ChartLineItem>(ChartZValue::Axes, theme.axisColor, axisTickWidth), pos });
}
std::vector<double> 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);

View file

@ -37,6 +37,7 @@ public:
protected:
StatsAxis(StatsView &view, const QString &title, bool horizontal, bool labelsBetweenTicks);
const StatsTheme &theme; // Initialized once in constructor.
ChartItemPtr<ChartLineItem> line;
QString title;
double titleWidth;

View file

@ -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<const StatsTheme *> statsThemes = { &statsThemeLight };

View file

@ -3,31 +3,53 @@
#ifndef STATSCOLORS_H
#define STATSCOLORS_H
#include <vector>
#include <QColor>
#include <QString>
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<const StatsTheme *> statsThemes;
#endif

View file

@ -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<ChartLineItem>(ChartZValue::Grid, gridColor, gridWidth));
lines.push_back(view.createChartItem<ChartLineItem>(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<ChartLineItem>(ChartZValue::Grid, gridColor, gridWidth));
lines.push_back(view.createChartItem<ChartLineItem>(ChartZValue::Grid, theme.gridColor, gridWidth));
lines.back()->setLine(QPointF(xtics.front(), y), QPointF(xtics.back(), y));
}
}

View file

@ -7,6 +7,7 @@
#include <vector>
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<ChartItemPtr<ChartLineItem>> lines;
};

View file

@ -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)
{
}

View file

@ -10,6 +10,7 @@
#include <QPointF>
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);
};

View file

@ -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<ChartRectLineItem>(ChartZValue::Selection, selectionLassoColor, selectionLassoWidth);
selectionRect = createChartItem<ChartRectLineItem>(ChartZValue::Selection, currentTheme->selectionLassoColor, selectionLassoWidth);
selectionModifier = modifier;
oldSelection = modifier.ctrl ? getDiveSelection() : std::vector<dive *>();
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<ChartTextItem>(ChartZValue::Legend, titleFont, s);
title->setColor(darkLabelColor);
title->setColor(currentTheme->darkLabelColor);
}
void StatsView::updateTitlePos()
@ -1128,10 +1141,10 @@ void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
if (categoryVariable->type() == StatsVariable::Type::Numeric) {
double mean = categoryVariable->mean(dives);
if (!std::isnan(mean))
meanMarker = createChartItem<HistogramMarker>(mean, isHorizontal, meanMarkerColor, xAxis, yAxis);
meanMarker = createChartItem<HistogramMarker>(mean, isHorizontal, currentTheme->meanMarkerColor, xAxis, yAxis);
double median = categoryVariable->quartiles(dives).q2;
if (!std::isnan(median))
medianMarker = createChartItem<HistogramMarker>(median, isHorizontal, medianMarkerColor, xAxis, yAxis);
medianMarker = createChartItem<HistogramMarker>(median, isHorizontal, currentTheme->medianMarkerColor, xAxis, yAxis);
}
}

View file

@ -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<std::unique_ptr<StatsSeries>> series;
std::unique_ptr<StatsGrid> grid;