summaryrefslogtreecommitdiffstats
path: root/stats
diff options
context:
space:
mode:
Diffstat (limited to 'stats')
-rw-r--r--stats/barseries.cpp2
-rw-r--r--stats/chartitem.cpp24
-rw-r--r--stats/chartitem.h53
-rw-r--r--stats/statshelper.h79
4 files changed, 138 insertions, 20 deletions
diff --git a/stats/barseries.cpp b/stats/barseries.cpp
index d7211212e..7218c3255 100644
--- a/stats/barseries.cpp
+++ b/stats/barseries.cpp
@@ -97,7 +97,7 @@ BarSeries::BarLabel::BarLabel(StatsView &view, const std::vector<QString> &label
void BarSeries::BarLabel::setVisible(bool visible)
{
- // item->setVisible(visible); TODO!
+ item->setVisible(visible);
}
void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount)
diff --git a/stats/chartitem.cpp b/stats/chartitem.cpp
index bb01c6e2c..ce5e6860c 100644
--- a/stats/chartitem.cpp
+++ b/stats/chartitem.cpp
@@ -31,7 +31,7 @@ QSizeF ChartItem::sceneSize() const
return view.size();
}
-ChartPixmapItem::ChartPixmapItem(StatsView &v, ChartZValue z) : ChartItem(v, z),
+ChartPixmapItem::ChartPixmapItem(StatsView &v, ChartZValue z) : HideableChartItem(v, z),
positionDirty(false), textureDirty(false)
{
}
@@ -56,7 +56,7 @@ void ChartPixmapItem::setPositionDirty()
void ChartPixmapItem::render()
{
if (!node) {
- node.reset(view.w()->createImageNode());
+ createNode(view.w()->createImageNode());
view.addQSGNode(node.get(), zValue);
}
if (!img) {
@@ -65,11 +65,11 @@ void ChartPixmapItem::render()
}
if (textureDirty) {
texture.reset(view.w()->createTextureFromImage(*img, QQuickWindow::TextureHasAlphaChannel));
- node->setTexture(texture.get());
+ node->node->setTexture(texture.get());
textureDirty = false;
}
if (positionDirty) {
- node->setRect(rect);
+ node->node->setRect(rect);
positionDirty = false;
}
}
@@ -151,7 +151,7 @@ void ChartTextItem::setColor(const QColor &c)
setTextureDirty();
}
-ChartLineItem::ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width) : ChartItem(v, z),
+ChartLineItem::ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width) : HideableChartItem(v, z),
color(color), width(width), positionDirty(false), materialDirty(false)
{
}
@@ -172,7 +172,7 @@ void ChartLineItem::render()
geometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2));
geometry->setDrawingMode(QSGGeometry::DrawLines);
material.reset(new QSGFlatColorMaterial);
- node.reset(new QSGGeometryNode);
+ createNode();
node->setGeometry(geometry.get());
node->setMaterial(material.get());
view.addQSGNode(node.get(), zValue);
@@ -204,7 +204,7 @@ void ChartLineItem::setLine(QPointF fromIn, QPointF toIn)
view.registerDirtyChartItem(*this);
}
-ChartBarItem::ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal) : ChartItem(v, z),
+ChartBarItem::ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal) : HideableChartItem(v, z),
borderWidth(borderWidth), horizontal(horizontal),
positionDirty(false), colorDirty(false)
{
@@ -217,7 +217,7 @@ ChartBarItem::~ChartBarItem()
void ChartBarItem::render()
{
if (!node) {
- node.reset(view.w()->createRectangleNode());
+ createNode(view.w()->createRectangleNode());
borderGeometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4));
borderGeometry->setDrawingMode(QSGGeometry::DrawLineLoop);
@@ -227,19 +227,20 @@ void ChartBarItem::render()
borderNode->setGeometry(borderGeometry.get());
borderNode->setMaterial(borderMaterial.get());
- node->appendChildNode(borderNode.get());
+ node->node->appendChildNode(borderNode.get());
view.addQSGNode(node.get(), zValue);
positionDirty = colorDirty = true;
}
if (colorDirty) {
- node->setColor(color);
+ node->node->setColor(color);
borderMaterial->setColor(borderColor);
+ node->node->markDirty(QSGNode::DirtyMaterial);
borderNode->markDirty(QSGNode::DirtyMaterial);
}
if (positionDirty) {
- node->setRect(rect);
+ node->node->setRect(rect);
auto vertices = borderGeometry->vertexDataAsPoint2D();
if (horizontal) {
setPoint(vertices[0], rect.topLeft());
@@ -252,6 +253,7 @@ void ChartBarItem::render()
setPoint(vertices[2], rect.topRight());
setPoint(vertices[3], rect.bottomRight());
}
+ node->node->markDirty(QSGNode::DirtyGeometry);
borderNode->markDirty(QSGNode::DirtyGeometry);
}
diff --git a/stats/chartitem.h b/stats/chartitem.h
index 93c80547f..a87d5fe10 100644
--- a/stats/chartitem.h
+++ b/stats/chartitem.h
@@ -4,6 +4,8 @@
#ifndef CHART_ITEM_H
#define CHART_ITEM_H
+#include "statshelper.h"
+
#include <memory>
#include <QPainter>
@@ -18,19 +20,35 @@ enum class ChartZValue : int;
class ChartItem {
public:
- ChartItem(StatsView &v, ChartZValue z);
- virtual ~ChartItem();
virtual void render() = 0; // Only call on render thread!
bool dirty; // If true, call render() when rebuilding the scene
ChartItem *dirtyPrev, *dirtyNext; // Double linked list of dirty items
const ChartZValue zValue;
protected:
+ ChartItem(StatsView &v, ChartZValue z);
+ virtual ~ChartItem();
QSizeF sceneSize() const;
StatsView &view;
};
+template <typename Node>
+class HideableChartItem : public ChartItem {
+protected:
+ HideableChartItem(StatsView &v, ChartZValue z);
+ std::unique_ptr<Node> node;
+ bool visible; // Argh. If visibility is set before node is created, we have to cache it.
+ template<class... Args>
+ void createNode(Args&&... args); // Call to create node with visibility flag.
+public:
+ void setVisible(bool visible);
+};
+
+// A shortcut for ChartItems based on a hideable proxy item
+template <typename Node>
+using HideableChartProxyItem = HideableChartItem<HideableQSGNode<QSGProxyNode<Node>>>;
+
// A chart item that blits a precalculated pixmap onto the scene.
-class ChartPixmapItem : public ChartItem {
+class ChartPixmapItem : public HideableChartProxyItem<QSGImageNode> {
public:
ChartPixmapItem(StatsView &v, ChartZValue z);
~ChartPixmapItem();
@@ -48,7 +66,6 @@ private:
QRectF rect;
bool positionDirty; // true if the position changed since last render
bool textureDirty; // true if the pixmap changed since last render
- std::unique_ptr<QSGImageNode> node;
std::unique_ptr<QSGTexture> texture;
};
@@ -80,7 +97,7 @@ private:
std::vector<Item> items;
};
-class ChartLineItem : public ChartItem {
+class ChartLineItem : public HideableChartItem<HideableQSGNode<QSGGeometryNode>> {
public:
ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width);
~ChartLineItem();
@@ -93,13 +110,12 @@ private:
bool horizontal;
bool positionDirty;
bool materialDirty;
- std::unique_ptr<QSGGeometryNode> node;
std::unique_ptr<QSGFlatColorMaterial> material;
std::unique_ptr<QSGGeometry> geometry;
};
// A bar in a bar chart: a rectangle bordered by lines.
-class ChartBarItem : public ChartItem {
+class ChartBarItem : public HideableChartProxyItem<QSGRectangleNode> {
public:
ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal);
~ChartBarItem();
@@ -114,10 +130,31 @@ private:
bool horizontal;
bool positionDirty;
bool colorDirty;
- std::unique_ptr<QSGRectangleNode> node;
std::unique_ptr<QSGGeometryNode> borderNode;
std::unique_ptr<QSGFlatColorMaterial> borderMaterial;
std::unique_ptr<QSGGeometry> borderGeometry;
};
+// Implementation detail of templates - move to serparate header file
+template <typename Node>
+void HideableChartItem<Node>::setVisible(bool visibleIn)
+{
+ visible = visibleIn;
+ if (node)
+ node->setVisible(visible);
+}
+
+template <typename Node>
+template<class... Args>
+void HideableChartItem<Node>::createNode(Args&&... args)
+{
+ node.reset(new Node(visible, std::forward<Args>(args)...));
+}
+
+template <typename Node>
+HideableChartItem<Node>::HideableChartItem(StatsView &v, ChartZValue z) : ChartItem(v, z),
+ visible(true)
+{
+}
+
#endif
diff --git a/stats/statshelper.h b/stats/statshelper.h
index 0ea39763d..29dc083a4 100644
--- a/stats/statshelper.h
+++ b/stats/statshelper.h
@@ -4,9 +4,11 @@
// is for historical reasons to ease transition from QtCharts
// and might be removed.
#ifndef STATSHELPER_H
+#define STATSHELPER_H
#include <memory>
#include <QGraphicsScene>
+#include <QSGNode>
template <typename T, class... Args>
T *createItem(QGraphicsScene *scene, Args&&... args)
@@ -22,4 +24,81 @@ std::unique_ptr<T> createItemPtr(QGraphicsScene *scene, Args&&... args)
return std::unique_ptr<T>(createItem<T>(scene, std::forward<Args>(args)...));
}
+// In general, we want chart items to be hideable. For example to show/hide
+// labels on demand. Very sadly, the QSG API is absolutely terrible with
+// respect to temporarily disabling. Instead of simply having a flag,
+// a QSGNode is queried using the "isSubtreeBlocked()" virtual function(!).
+//
+// Not only is this a slow operation performed on every single node, it
+// also is often not possible to override this function: For improved
+// performance, the documentation recommends to create QSG nodes via
+// QQuickWindow. This provides nodes optimized for the actual hardware.
+// However, this obviously means that these nodes cannot be derived from!
+//
+// In that case, there are two possibilities: Add a proxy node with an
+// overridden "isSubtreeBlocked()" function or remove the node from the
+// scene. The former was chosen here, because it is less complex.
+//
+// The following slightly cryptic templates are used to unify the two
+// cases: The QSGNode is generated by our own code or the QSGNode is
+// obtained from QQuickWindow.
+//
+// The "HideableQSGNode<Node>" template augments the QSGNode "Node"
+// by a "setVisible()" function and overrides "isSubtreeBlocked()"
+//
+// The "QSGProxyNode<Node>" template is a QSGNode with a single
+// child of type "Node".
+//
+// Thus, if the node can be created, use:
+// HideableQSGNode<NodeTypeThatCanBeCreated> node
+// and if the node can only be obtained from QQuickWindow, use:
+// HideableQSGNode<QSGProxyNode<NodeThatCantBeCreated>> node
+// The latter should obviously be typedef-ed.
+//
+// Yes, that's all horrible, but if nothing else it teaches us about
+// composition.
+template <typename Node>
+class HideableQSGNode : public Node {
+ bool hidden;
+ bool isSubtreeBlocked() const override final;
+public:
+ template<class... Args>
+ HideableQSGNode(bool visible, Args&&... args);
+ void setVisible(bool visible);
+};
+
+template <typename Node>
+class QSGProxyNode : public QSGNode {
+public:
+ std::unique_ptr<Node> node;
+ QSGProxyNode(Node *node);
+};
+
+// Implementation detail of templates - move to serparate header file
+template <typename Node>
+QSGProxyNode<Node>::QSGProxyNode(Node *node) : node(node)
+{
+ appendChildNode(node);
+}
+
+template <typename Node>
+bool HideableQSGNode<Node>::isSubtreeBlocked() const
+{
+ return hidden;
+}
+
+template <typename Node>
+template<class... Args>
+HideableQSGNode<Node>::HideableQSGNode(bool visible, Args&&... args) :
+ Node(std::forward<Args>(args)...),
+ hidden(!visible)
+{
+}
+
+template <typename Node>
+void HideableQSGNode<Node>::setVisible(bool visible)
+{
+ hidden = !visible;
+}
+
#endif