stats: make number of z-levels dynamic

Other users of the qtquick code might have different needs
for z-levels.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2023-04-16 12:59:19 +02:00
parent 4c43764b36
commit 533cff96f3
5 changed files with 50 additions and 44 deletions

View file

@ -19,7 +19,7 @@ static int round_up(double f)
return static_cast<int>(ceil(f)); return static_cast<int>(ceil(f));
} }
ChartItem::ChartItem(StatsView &v, ChartZValue z) : ChartItem::ChartItem(StatsView &v, size_t z) :
dirty(false), prev(nullptr), next(nullptr), dirty(false), prev(nullptr), next(nullptr),
zValue(z), view(v) zValue(z), view(v)
{ {
@ -42,7 +42,7 @@ void ChartItem::markDirty()
view.registerDirtyChartItem(*this); view.registerDirtyChartItem(*this);
} }
ChartPixmapItem::ChartPixmapItem(StatsView &v, ChartZValue z) : HideableChartItem(v, z), ChartPixmapItem::ChartPixmapItem(StatsView &v, size_t z) : HideableChartItem(v, z),
positionDirty(false), textureDirty(false) positionDirty(false), textureDirty(false)
{ {
} }
@ -111,7 +111,7 @@ QRectF ChartPixmapItem::getRect() const
static const int scatterItemDiameter = 10; static const int scatterItemDiameter = 10;
static const int scatterItemBorder = 1; static const int scatterItemBorder = 1;
ChartScatterItem::ChartScatterItem(StatsView &v, ChartZValue z, const StatsTheme &theme, bool selected) : HideableChartItem(v, z), ChartScatterItem::ChartScatterItem(StatsView &v, size_t z, const StatsTheme &theme, bool selected) : HideableChartItem(v, z),
theme(theme), theme(theme),
positionDirty(false), textureDirty(false), positionDirty(false), textureDirty(false),
highlight(selected ? Highlight::Selected : Highlight::Unselected) highlight(selected ? Highlight::Selected : Highlight::Unselected)
@ -214,7 +214,7 @@ QRectF ChartScatterItem::getRect() const
return rect; return rect;
} }
ChartRectItem::ChartRectItem(StatsView &v, ChartZValue z, ChartRectItem::ChartRectItem(StatsView &v, size_t z,
const QPen &pen, const QBrush &brush, double radius) : ChartPixmapItem(v, z), const QPen &pen, const QBrush &brush, double radius) : ChartPixmapItem(v, z),
pen(pen), brush(brush), radius(radius) pen(pen), brush(brush), radius(radius)
{ {
@ -236,7 +236,7 @@ void ChartRectItem::resize(QSizeF size)
painter->drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize); painter->drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
} }
ChartTextItem::ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const std::vector<QString> &text, bool center) : ChartTextItem::ChartTextItem(StatsView &v, size_t z, const QFont &f, const std::vector<QString> &text, bool center) :
ChartPixmapItem(v, z), f(f), center(center) ChartPixmapItem(v, z), f(f), center(center)
{ {
QFontMetrics fm(f); QFontMetrics fm(f);
@ -254,7 +254,7 @@ ChartTextItem::ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const
resize(QSizeF(totalWidth, totalHeight)); resize(QSizeF(totalWidth, totalHeight));
} }
ChartTextItem::ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const QString &text) : ChartTextItem::ChartTextItem(StatsView &v, size_t z, const QFont &f, const QString &text) :
ChartTextItem(v, z, f, std::vector<QString>({ text }), true) ChartTextItem(v, z, f, std::vector<QString>({ text }), true)
{ {
} }
@ -280,7 +280,7 @@ void ChartTextItem::setColor(const QColor &c, const QColor &background)
setTextureDirty(); setTextureDirty();
} }
ChartPieItem::ChartPieItem(StatsView &v, ChartZValue z, const StatsTheme &theme, double borderWidth) : ChartPixmapItem(v, z), ChartPieItem::ChartPieItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth) : ChartPixmapItem(v, z),
theme(theme), theme(theme),
borderWidth(borderWidth) borderWidth(borderWidth)
{ {
@ -322,7 +322,7 @@ void ChartPieItem::resize(QSizeF size)
img->fill(Qt::transparent); img->fill(Qt::transparent);
} }
ChartLineItemBase::ChartLineItemBase(StatsView &v, ChartZValue z, QColor color, double width) : HideableChartItem(v, z), ChartLineItemBase::ChartLineItemBase(StatsView &v, size_t z, QColor color, double width) : HideableChartItem(v, z),
color(color), width(width), positionDirty(false), materialDirty(false) color(color), width(width), positionDirty(false), materialDirty(false)
{ {
} }
@ -416,7 +416,7 @@ void ChartRectLineItem::render()
positionDirty = materialDirty = false; positionDirty = materialDirty = false;
} }
ChartBarItem::ChartBarItem(StatsView &v, ChartZValue z, const StatsTheme &theme, double borderWidth) : HideableChartItem(v, z), ChartBarItem::ChartBarItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth) : HideableChartItem(v, z),
theme(theme), theme(theme),
borderWidth(borderWidth), selected(false), borderWidth(borderWidth), selected(false),
positionDirty(false), colorDirty(false), selectedDirty(false) positionDirty(false), colorDirty(false), selectedDirty(false)
@ -548,7 +548,7 @@ QRectF ChartBarItem::getRect() const
return rect; return rect;
} }
ChartBoxItem::ChartBoxItem(StatsView &v, ChartZValue z, const StatsTheme &theme, double borderWidth) : ChartBoxItem::ChartBoxItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth) :
ChartBarItem(v, z, theme, borderWidth) ChartBarItem(v, z, theme, borderWidth)
{ {
} }

View file

@ -18,7 +18,6 @@ class QSGTexture;
class QSGTextureMaterial; class QSGTextureMaterial;
class StatsTheme; class StatsTheme;
class StatsView; class StatsView;
enum class ChartZValue : int;
class ChartItem { class ChartItem {
public: public:
@ -26,10 +25,10 @@ public:
virtual void render() = 0; virtual void render() = 0;
bool dirty; // If true, call render() when rebuilding the scene bool dirty; // If true, call render() when rebuilding the scene
ChartItem *prev, *next; // Double linked list of items ChartItem *prev, *next; // Double linked list of items
const ChartZValue zValue; const size_t zValue;
virtual ~ChartItem(); // Attention: must only be called by render thread. virtual ~ChartItem(); // Attention: must only be called by render thread.
protected: protected:
ChartItem(StatsView &v, ChartZValue z); ChartItem(StatsView &v, size_t z);
QSizeF sceneSize() const; QSizeF sceneSize() const;
StatsView &view; StatsView &view;
void markDirty(); void markDirty();
@ -38,7 +37,7 @@ protected:
template <typename Node> template <typename Node>
class HideableChartItem : public ChartItem { class HideableChartItem : public ChartItem {
protected: protected:
HideableChartItem(StatsView &v, ChartZValue z); HideableChartItem(StatsView &v, size_t z);
std::unique_ptr<Node> node; std::unique_ptr<Node> node;
bool visible; bool visible;
bool visibleChanged; bool visibleChanged;
@ -56,7 +55,7 @@ using HideableChartProxyItem = HideableChartItem<HideableQSGNode<QSGProxyNode<No
// A chart item that blits a precalculated pixmap onto the scene. // A chart item that blits a precalculated pixmap onto the scene.
class ChartPixmapItem : public HideableChartProxyItem<QSGImageNode> { class ChartPixmapItem : public HideableChartProxyItem<QSGImageNode> {
public: public:
ChartPixmapItem(StatsView &v, ChartZValue z); ChartPixmapItem(StatsView &v, size_t z);
~ChartPixmapItem(); ~ChartPixmapItem();
void setPos(QPointF pos); void setPos(QPointF pos);
@ -78,7 +77,7 @@ private:
// Draw a rectangular background after resize. Children are responsible for calling update(). // Draw a rectangular background after resize. Children are responsible for calling update().
class ChartRectItem : public ChartPixmapItem { class ChartRectItem : public ChartPixmapItem {
public: public:
ChartRectItem(StatsView &v, ChartZValue z, const QPen &pen, const QBrush &brush, double radius); ChartRectItem(StatsView &v, size_t z, const QPen &pen, const QBrush &brush, double radius);
~ChartRectItem(); ~ChartRectItem();
void resize(QSizeF size); void resize(QSizeF size);
private: private:
@ -90,8 +89,8 @@ private:
// Attention: text is only drawn after calling setColor()! // Attention: text is only drawn after calling setColor()!
class ChartTextItem : public ChartPixmapItem { class ChartTextItem : public ChartPixmapItem {
public: public:
ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const std::vector<QString> &text, bool center); ChartTextItem(StatsView &v, size_t z, const QFont &f, const std::vector<QString> &text, bool center);
ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const QString &text); ChartTextItem(StatsView &v, size_t z, const QFont &f, const QString &text);
void setColor(const QColor &color); // Draw on transparent background void setColor(const QColor &color); // Draw on transparent background
void setColor(const QColor &color, const QColor &background); // Fill rectangle with given background color void setColor(const QColor &color, const QColor &background); // Fill rectangle with given background color
private: private:
@ -108,7 +107,7 @@ private:
// A pie chart item: draws disk segments onto a pixmap. // A pie chart item: draws disk segments onto a pixmap.
class ChartPieItem : public ChartPixmapItem { class ChartPieItem : public ChartPixmapItem {
public: public:
ChartPieItem(StatsView &v, ChartZValue z, const StatsTheme &theme, double borderWidth); ChartPieItem(StatsView &v, size_t z, const StatsTheme &theme, 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); // from and to are relative (0-1 is full disk).
void resize(QSizeF size); // As in base class, but clears the canvas void resize(QSizeF size); // As in base class, but clears the canvas
private: private:
@ -119,7 +118,7 @@ private:
// Common data for line and rect items. Both are represented by two points. // Common data for line and rect items. Both are represented by two points.
class ChartLineItemBase : public HideableChartItem<HideableQSGNode<QSGGeometryNode>> { class ChartLineItemBase : public HideableChartItem<HideableQSGNode<QSGGeometryNode>> {
public: public:
ChartLineItemBase(StatsView &v, ChartZValue z, QColor color, double width); ChartLineItemBase(StatsView &v, size_t z, QColor color, double width);
~ChartLineItemBase(); ~ChartLineItemBase();
void setLine(QPointF from, QPointF to); void setLine(QPointF from, QPointF to);
protected: protected:
@ -148,7 +147,7 @@ public:
// A bar in a bar chart: a rectangle bordered by lines. // A bar in a bar chart: a rectangle bordered by lines.
class ChartBarItem : public HideableChartProxyItem<QSGRectangleNode> { class ChartBarItem : public HideableChartProxyItem<QSGRectangleNode> {
public: public:
ChartBarItem(StatsView &v, ChartZValue z, const StatsTheme &theme, double borderWidth); ChartBarItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth);
~ChartBarItem(); ~ChartBarItem();
void setColor(QColor color, QColor borderColor); void setColor(QColor color, QColor borderColor);
void setRect(const QRectF &rect); void setRect(const QRectF &rect);
@ -178,7 +177,7 @@ private:
// A box-and-whiskers item. This is a bit lazy: derive from the bar item and add whiskers. // A box-and-whiskers item. This is a bit lazy: derive from the bar item and add whiskers.
class ChartBoxItem : public ChartBarItem { class ChartBoxItem : public ChartBarItem {
public: public:
ChartBoxItem(StatsView &v, ChartZValue z, const StatsTheme &theme, double borderWidth); ChartBoxItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth);
~ChartBoxItem(); ~ChartBoxItem();
void setBox(const QRectF &rect, double min, double max, double median); // The rect describes Q1, Q3. 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. QRectF getRect() const; // Note: this extends the center rectangle to include the whiskers.
@ -196,7 +195,7 @@ private:
// scatter item here, but so it is for now. // scatter item here, but so it is for now.
class ChartScatterItem : public HideableChartProxyItem<QSGImageNode> { class ChartScatterItem : public HideableChartProxyItem<QSGImageNode> {
public: public:
ChartScatterItem(StatsView &v, ChartZValue z, const StatsTheme &theme, bool selected); ChartScatterItem(StatsView &v, size_t z, const StatsTheme &theme, bool selected);
~ChartScatterItem(); ~ChartScatterItem();
// Currently, there is no highlighted and selected status. // Currently, there is no highlighted and selected status.
@ -240,7 +239,7 @@ void HideableChartItem<Node>::createNode(Args&&... args)
} }
template <typename Node> template <typename Node>
HideableChartItem<Node>::HideableChartItem(StatsView &v, ChartZValue z) : ChartItem(v, z), HideableChartItem<Node>::HideableChartItem(StatsView &v, size_t z) : ChartItem(v, z),
visible(true), visibleChanged(false) visible(true), visibleChanged(false)
{ {
} }

View file

@ -21,7 +21,6 @@
#include "core/selection.h" #include "core/selection.h"
#include "core/trip.h" #include "core/trip.h"
#include <array> // for std::array
#include <cmath> #include <cmath>
#include <QQuickItem> #include <QQuickItem>
#include <QQuickWindow> #include <QQuickWindow>
@ -35,6 +34,7 @@ static const double titleBorder = 2.0; // Border between title and chart
static const double selectionLassoWidth = 2.0; // Border between title and chart static const double selectionLassoWidth = 2.0; // Border between title and chart
StatsView::StatsView(QQuickItem *parent) : QQuickItem(parent), StatsView::StatsView(QQuickItem *parent) : QQuickItem(parent),
maxZ(ChartZValue::Count),
backgroundDirty(true), backgroundDirty(true),
currentTheme(&getStatsTheme(false)), currentTheme(&getStatsTheme(false)),
backgroundColor(currentTheme->backgroundColor), backgroundColor(currentTheme->backgroundColor),
@ -129,16 +129,18 @@ using ZNode = HideableQSGNode<QSGNode>;
class RootNode : public QSGNode class RootNode : public QSGNode
{ {
public: public:
RootNode(StatsView &view, QColor backgroundColor); RootNode(StatsView &view, QColor backgroundColor, size_t maxZ);
~RootNode(); ~RootNode();
StatsView &view; StatsView &view;
std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background
// We entertain one node per Z-level. // We entertain one node per Z-level.
std::array<std::unique_ptr<ZNode>, (size_t)ChartZValue::Count> zNodes; std::vector<std::unique_ptr<ZNode>> zNodes;
}; };
RootNode::RootNode(StatsView &view, QColor backgroundColor) : view(view) RootNode::RootNode(StatsView &view, QColor backgroundColor, size_t maxZ) : view(view)
{ {
zNodes.resize(maxZ);
// Add a background rectangle with a solid color. This could // Add a background rectangle with a solid color. This could
// also be done on the widget level, but would have to be done // also be done on the widget level, but would have to be done
// separately for desktop and mobile, so do it here. // separately for desktop and mobile, so do it here.
@ -172,7 +174,7 @@ QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNod
// This is just a copy of what is found in Qt's documentation. // This is just a copy of what is found in Qt's documentation.
RootNode *n = static_cast<RootNode *>(oldNode); RootNode *n = static_cast<RootNode *>(oldNode);
if (!n) if (!n)
n = rootNode = new RootNode(*this, backgroundColor); n = rootNode = new RootNode(*this, backgroundColor, maxZ);
// Delete all chart items that are marked for deletion. // Delete all chart items that are marked for deletion.
freeDeletedChartItems(); freeDeletedChartItems();
@ -217,9 +219,9 @@ void StatsView::emergencyShutdown()
rootNode = nullptr; rootNode = nullptr;
} }
void StatsView::addQSGNode(QSGNode *node, ChartZValue z) void StatsView::addQSGNode(QSGNode *node, size_t z)
{ {
int idx = std::clamp((int)z, 0, (int)ChartZValue::Count - 1); size_t idx = std::clamp(z, (size_t)0, maxZ);
rootNode->zNodes[idx]->appendChildNode(node); rootNode->zNodes[idx]->appendChildNode(node);
} }
@ -617,7 +619,7 @@ void StatsView::updateFeatures()
// For labels, we are brutal: simply show/hide the whole z-level with the labels // For labels, we are brutal: simply show/hide the whole z-level with the labels
if (rootNode) if (rootNode)
rootNode->zNodes[(int)ChartZValue::SeriesLabels]->setVisible(state.labels); rootNode->zNodes[ChartZValue::SeriesLabels]->setVisible(state.labels);
if (meanMarker) if (meanMarker)
meanMarker->setVisible(state.mean); meanMarker->setVisible(state.mean);

View file

@ -34,7 +34,6 @@ class QSGTexture;
class RootNode; // Internal implementation detail class RootNode; // Internal implementation detail
enum class ChartSubType : int; enum class ChartSubType : int;
enum class ChartZValue : int;
enum class StatsOperation : int; enum class StatsOperation : int;
enum class ChartSortMode : int; enum class ChartSortMode : int;
@ -56,7 +55,7 @@ public:
void setBackgroundColor(QColor color); // Chart must be replot for color to become effective. void setBackgroundColor(QColor color); // Chart must be replot for color to become effective.
void setTheme(bool dark); // Chart must be replot for theme to become effective. void setTheme(bool dark); // Chart must be replot for theme to become effective.
const StatsTheme &getCurrentTheme() const; const StatsTheme &getCurrentTheme() const;
void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread! void addQSGNode(QSGNode *node, size_t z); // Must only be called in render thread!
void registerChartItem(ChartItem &item); void registerChartItem(ChartItem &item);
void registerDirtyChartItem(ChartItem &item); void registerDirtyChartItem(ChartItem &item);
void emergencyShutdown(); // Called when QQuick decides to delete our root node. void emergencyShutdown(); // Called when QQuick decides to delete our root node.
@ -76,6 +75,7 @@ private slots:
void divesSelected(const QVector<dive *> &dives); void divesSelected(const QVector<dive *> &dives);
private: private:
// QtQuick related things // QtQuick related things
size_t maxZ;
bool backgroundDirty; bool backgroundDirty;
QRectF plotRect; QRectF plotRect;
QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override; QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override;

View file

@ -5,16 +5,21 @@
// drawn in order of addition to the scene. // drawn in order of addition to the scene.
#ifndef ZVALUES_H #ifndef ZVALUES_H
enum class ChartZValue { // Encapsulating an enum in a struct is stupid, but allows us
Grid = 0, // to not poison the namespace and yet autoconvert to int
Series, // (in constrast to enum class). enum is so broken!
Axes, struct ChartZValue {
SeriesLabels, enum ZValues {
ChartFeatures, // quartile markers and regression lines Grid = 0,
Selection, Series,
InformationBox, Axes,
Legend, SeriesLabels,
Count ChartFeatures, // quartile markers and regression lines
Selection,
InformationBox,
Legend,
Count
};
}; };
#endif #endif