diff options
Diffstat (limited to 'stats/statshelper.h')
| -rw-r--r-- | stats/statshelper.h | 135 |
1 files changed, 122 insertions, 13 deletions
diff --git a/stats/statshelper.h b/stats/statshelper.h index 0ea39763d..6b4a30ab5 100644 --- a/stats/statshelper.h +++ b/stats/statshelper.h @@ -1,25 +1,134 @@ // SPDX-License-Identifier: GPL-2.0 -// Helper functions to render the stats. Currently only -// contains a small template to create scene-items. This -// is for historical reasons to ease transition from QtCharts -// and might be removed. +// Helper functions to render the stats. Currently contains +// QSGNode template jugglery to overcome API flaws. #ifndef STATSHELPER_H +#define STATSHELPER_H #include <memory> -#include <QGraphicsScene> +#include <QSGNode> -template <typename T, class... Args> -T *createItem(QGraphicsScene *scene, Args&&... args) +// A stupid pointer class that initializes to null and can be copy +// assigned. This is for historical reasons: unique_ptrs to ChartItems +// were replaced by plain pointers. Instead of nulling the plain pointers +// in the constructors, use this. Ultimately, we might think about making +// this thing smarter, once removal of individual ChartItems is implemented. +template <typename T> +class ChartItemPtr { + friend class StatsView; // Only the stats view can create these pointers + T *ptr; + ChartItemPtr(T *ptr) : ptr(ptr) + { + } +public: + ChartItemPtr() : ptr(nullptr) + { + } + ChartItemPtr(const ChartItemPtr &p) : ptr(p.ptr) + { + } + void reset() + { + ptr = nullptr; + } + ChartItemPtr &operator=(const ChartItemPtr &p) + { + ptr = p.ptr; + return *this; + } + operator bool() const + { + return !!ptr; + } + bool operator!() const + { + return !ptr; + } + T &operator*() const + { + return *ptr; + } + T *operator->() const + { + return ptr; + } +}; + +// 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) { - T *res = new T(std::forward<Args>(args)...); - scene->addItem(res); - return res; } -template <typename T, class... Args> -std::unique_ptr<T> createItemPtr(QGraphicsScene *scene, Args&&... args) +template <typename Node> +void HideableQSGNode<Node>::setVisible(bool visible) { - return std::unique_ptr<T>(createItem<T>(scene, std::forward<Args>(args)...)); + hidden = !visible; + Node::markDirty(QSGNode::DirtySubtreeBlocked); } #endif |