// SPDX-License-Identifier: GPL-2.0 // Helper functions to render the stats. Includes // QSGNode template jugglery to overcome API flaws. #ifndef STATSHELPER_H #define STATSHELPER_H #include #include #include // Round positions to integer values to avoid ugly artifacts QPointF roundPos(const QPointF &p); // 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 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" template augments the QSGNode "Node" // by a "setVisible()" function and overrides "isSubtreeBlocked()" // // The "QSGProxyNode" template is a QSGNode with a single // child of type "Node". // // Thus, if the node can be created, use: // HideableQSGNode node // and if the node can only be obtained from QQuickWindow, use: // HideableQSGNode> node // The latter should obviously be typedef-ed. // // Yes, that's all horrible, but if nothing else it teaches us about // composition. template class HideableQSGNode : public Node { bool hidden; bool isSubtreeBlocked() const override final; public: template HideableQSGNode(bool visible, Args&&... args); void setVisible(bool visible); }; template class QSGProxyNode : public QSGNode { public: std::unique_ptr node; QSGProxyNode(Node *node); }; // Implementation detail of templates - move to serparate header file template QSGProxyNode::QSGProxyNode(Node *node) : node(node) { appendChildNode(node); } template bool HideableQSGNode::isSubtreeBlocked() const { return hidden; } template template HideableQSGNode::HideableQSGNode(bool visible, Args&&... args) : Node(std::forward(args)...), hidden(!visible) { } template void HideableQSGNode::setVisible(bool visible) { hidden = !visible; Node::markDirty(QSGNode::DirtySubtreeBlocked); } #endif