#pragma once #include #include #include #include #include #include #include #include namespace ZShell::components { class LineGraphRenderer; // A line graph with an optional grid, axis labels, y auto-scaling, a moving // average, smoothed Catmull-Rom curve, an area fill and a marker. Static: it draws // whatever `values` holds at the given x steps, unlike Sparkline which slides a // rolling buffer. Both share curve smoothing and device-pixel alignment through // internal::linegeometry. class LineGraph : public QCanvasPainterItem { Q_OBJECT QML_ELEMENT Q_PROPERTY( QVariantList values READ values WRITE setValues NOTIFY valuesChanged FINAL) Q_PROPERTY(int xSteps READ xSteps WRITE setXSteps NOTIFY xStepsChanged FINAL) Q_PROPERTY(qreal yMin READ yMin WRITE setYMin NOTIFY yMinChanged FINAL) Q_PROPERTY(qreal yMax READ yMax WRITE setYMax NOTIFY yMaxChanged FINAL) Q_PROPERTY( qreal yPaddingTop READ yPaddingTop WRITE setYPaddingTop NOTIFY yPaddingTopChanged FINAL) Q_PROPERTY( qreal yPaddingBottom READ yPaddingBottom WRITE setYPaddingBottom NOTIFY yPaddingBottomChanged FINAL) Q_PROPERTY( int verticalLineStep READ verticalLineStep WRITE setVerticalLineStep NOTIFY verticalLineStepChanged FINAL) Q_PROPERTY( int horizontalLineStep READ horizontalLineStep WRITE setHorizontalLineStep NOTIFY horizontalLineStepChanged FINAL) Q_PROPERTY( bool firstXLine READ firstXLine WRITE setFirstXLine NOTIFY firstXLineChanged FINAL) Q_PROPERTY( bool lastXLine READ lastXLine WRITE setLastXLine NOTIFY lastXLineChanged FINAL) Q_PROPERTY( int minHorizontalLines READ minHorizontalLines WRITE setMinHorizontalLines NOTIFY minHorizontalLinesChanged FINAL) Q_PROPERTY( XLabelMode xLabelMode READ xLabelMode WRITE setXLabelMode NOTIFY xLabelModeChanged FINAL) Q_PROPERTY( bool useTwelveHourClock READ useTwelveHourClock WRITE setUseTwelveHourClock NOTIFY useTwelveHourClockChanged FINAL) Q_PROPERTY( YLabelMode yLabelMode READ yLabelMode WRITE setYLabelMode NOTIFY yLabelModeChanged FINAL) Q_PROPERTY( QString yLabelSuffix READ yLabelSuffix WRITE setYLabelSuffix NOTIFY yLabelSuffixChanged FINAL) Q_PROPERTY( int yLabelDecimals READ yLabelDecimals WRITE setYLabelDecimals NOTIFY yLabelDecimalsChanged FINAL) Q_PROPERTY( qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged FINAL) Q_PROPERTY( bool curveSmoothing READ curveSmoothing WRITE setCurveSmoothing NOTIFY curveSmoothingChanged FINAL) Q_PROPERTY( qreal markerX READ markerX WRITE setMarkerX NOTIFY markerXChanged FINAL) Q_PROPERTY( qreal markerGap READ markerGap WRITE setMarkerGap NOTIFY markerGapChanged FINAL) Q_PROPERTY( QColor lineColor READ lineColor WRITE setLineColor NOTIFY lineColorChanged FINAL) Q_PROPERTY( QColor gridColor READ gridColor WRITE setGridColor NOTIFY gridColorChanged FINAL) Q_PROPERTY( QColor markerColor READ markerColor WRITE setMarkerColor NOTIFY markerColorChanged FINAL) Q_PROPERTY( QColor labelColor READ labelColor WRITE setLabelColor NOTIFY labelColorChanged FINAL) Q_PROPERTY( qreal gridAlpha READ gridAlpha WRITE setGridAlpha NOTIFY gridAlphaChanged FINAL) Q_PROPERTY( qreal labelAlpha READ labelAlpha WRITE setLabelAlpha NOTIFY labelAlphaChanged FINAL) Q_PROPERTY( qreal fillAlpha READ fillAlpha WRITE setFillAlpha NOTIFY fillAlphaChanged FINAL) Q_PROPERTY( qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL) Q_PROPERTY( qreal markerDotRadius READ markerDotRadius WRITE setMarkerDotRadius NOTIFY markerDotRadiusChanged FINAL) Q_PROPERTY( qreal labelFontSize READ labelFontSize WRITE setLabelFontSize NOTIFY labelFontSizeChanged FINAL) public: // How an x tick (integer index 0..xSteps) is labelled. Hour renders an // hour-of-day, honouring useTwelveHourClock. enum class XLabelMode { None, Number, Hour }; Q_ENUM(XLabelMode) // How a y tick value is labelled. Value renders the number rounded to // yLabelDecimals places, followed by yLabelSuffix. enum class YLabelMode { Off, Value }; Q_ENUM(YLabelMode) explicit LineGraph(QQuickItem* parent = nullptr); [[nodiscard]] QVariantList values() const; void setValues(const QVariantList& values); [[nodiscard]] int xSteps() const; void setXSteps(int xSteps); [[nodiscard]] qreal yMin() const; void setYMin(qreal value); [[nodiscard]] qreal yMax() const; void setYMax(qreal value); [[nodiscard]] qreal yPaddingTop() const; void setYPaddingTop(qreal value); [[nodiscard]] qreal yPaddingBottom() const; void setYPaddingBottom(qreal value); [[nodiscard]] int verticalLineStep() const; void setVerticalLineStep(int step); [[nodiscard]] int horizontalLineStep() const; void setHorizontalLineStep(int step); [[nodiscard]] bool firstXLine() const; void setFirstXLine(bool show); [[nodiscard]] bool lastXLine() const; void setLastXLine(bool show); [[nodiscard]] int minHorizontalLines() const; void setMinHorizontalLines(int count); [[nodiscard]] XLabelMode xLabelMode() const; void setXLabelMode(XLabelMode mode); [[nodiscard]] bool useTwelveHourClock() const; void setUseTwelveHourClock(bool use); [[nodiscard]] YLabelMode yLabelMode() const; void setYLabelMode(YLabelMode mode); [[nodiscard]] QString yLabelSuffix() const; void setYLabelSuffix(const QString& suffix); [[nodiscard]] int yLabelDecimals() const; void setYLabelDecimals(int decimals); [[nodiscard]] qreal smoothing() const; void setSmoothing(qreal smoothing); [[nodiscard]] bool curveSmoothing() const; void setCurveSmoothing(bool enabled); [[nodiscard]] qreal markerX() const; void setMarkerX(qreal markerX); [[nodiscard]] qreal markerGap() const; void setMarkerGap(qreal gap); [[nodiscard]] QColor lineColor() const; void setLineColor(const QColor& color); [[nodiscard]] QColor gridColor() const; void setGridColor(const QColor& color); [[nodiscard]] QColor markerColor() const; void setMarkerColor(const QColor& color); [[nodiscard]] QColor labelColor() const; void setLabelColor(const QColor& color); [[nodiscard]] qreal gridAlpha() const; void setGridAlpha(qreal alpha); [[nodiscard]] qreal labelAlpha() const; void setLabelAlpha(qreal alpha); [[nodiscard]] qreal fillAlpha() const; void setFillAlpha(qreal alpha); [[nodiscard]] qreal lineWidth() const; void setLineWidth(qreal width); [[nodiscard]] qreal markerDotRadius() const; void setMarkerDotRadius(qreal radius); [[nodiscard]] qreal labelFontSize() const; void setLabelFontSize(qreal size); signals: void valuesChanged(); void xStepsChanged(); void yMinChanged(); void yMaxChanged(); void yPaddingTopChanged(); void yPaddingBottomChanged(); void verticalLineStepChanged(); void horizontalLineStepChanged(); void firstXLineChanged(); void lastXLineChanged(); void minHorizontalLinesChanged(); void xLabelModeChanged(); void useTwelveHourClockChanged(); void yLabelModeChanged(); void yLabelSuffixChanged(); void yLabelDecimalsChanged(); void smoothingChanged(); void curveSmoothingChanged(); void markerXChanged(); void markerGapChanged(); void lineColorChanged(); void gridColorChanged(); void markerColorChanged(); void labelColorChanged(); void gridAlphaChanged(); void labelAlphaChanged(); void fillAlphaChanged(); void lineWidthChanged(); void markerDotRadiusChanged(); void labelFontSizeChanged(); protected: [[nodiscard]] QCanvasPainterItemRenderer* createItemRenderer() const override; // Device-pixel snapping and stroke width are quantized in device pixels, so // they must use the ratio the window actually has. void itemChange(ItemChange change, const ItemChangeData& value) override; private: friend class LineGraphRenderer; QVariantList m_values; int m_xSteps = 24; qreal m_yMin = std::numeric_limits::quiet_NaN(); qreal m_yMax = std::numeric_limits::quiet_NaN(); qreal m_yPaddingTop = 0.0; qreal m_yPaddingBottom = 0.0; int m_verticalLineStep = 0; int m_horizontalLineStep = 0; bool m_firstXLine = true; bool m_lastXLine = false; int m_minHorizontalLines = 0; XLabelMode m_xLabelMode = XLabelMode::None; bool m_useTwelveHourClock = false; YLabelMode m_yLabelMode = YLabelMode::Off; QString m_yLabelSuffix; int m_yLabelDecimals = 0; qreal m_smoothing = 0.0; bool m_curveSmoothing = true; qreal m_markerX = -1.0; qreal m_markerGap = 2.0; QColor m_lineColor = Qt::white; QColor m_gridColor = Qt::white; QColor m_markerColor = Qt::white; QColor m_labelColor = Qt::white; qreal m_gridAlpha = 0.12; qreal m_labelAlpha = 0.6; qreal m_fillAlpha = 0.15; qreal m_lineWidth = 2.0; qreal m_markerDotRadius = 4.0; qreal m_labelFontSize = 8.0; qreal m_dpr = 1.0; }; } // namespace ZShell::components