#pragma once #include #include #include #include #include "internal/circularbuffer.hpp" namespace ZShell::components { class SparklineContent : public QQuickPaintedItem { Q_OBJECT public: explicit SparklineContent(QQuickItem* parent = nullptr); void paint(QPainter* painter) override; void setLine1(internal::CircularBuffer* buffer); void setLine2(internal::CircularBuffer* buffer); void setLine1Color(const QColor& color); void setLine2Color(const QColor& color); void setLine1FillAlpha(qreal alpha); void setLine2FillAlpha(qreal alpha); void setMaxValue(qreal value); void setLineWidth(qreal width); void setCurveSmoothing(bool enabled); void setHistoryLength(int length); void setSliding(bool sliding); void setViewportSize(qreal viewportWidth, qreal viewportHeight); // How often a new sample arrives, in milliseconds. With this set the slide // phase is read from the sample clock rather than from `progress`, which // makes the wrap exact; without it the phase is the value QML supplies. void setSlideInterval(qreal intervalMs); // Offsets the item for the slide. Kept here rather than in SparklineItem // because the quantization needs both stepX() and the window's dpr. void setSlideProgress(qreal progress); [[nodiscard]] qreal stepX() const; protected: // The stroke width, sample spacing and plot bounds are quantized in device // pixels, so they have to be built with the ratio the window actually has. void itemChange(ItemChange change, const ItemChangeData& value) override; private: struct LineGeometry { QPainterPath stroke; QPainterPath fill; bool hasData = false; }; [[nodiscard]] qreal slidePhase(qreal progress) const; void invalidate(); void rebuildIfNeeded(); [[nodiscard]] LineGeometry buildGeometry( internal::CircularBuffer* buffer) const; void connectBuffer(internal::CircularBuffer* buffer); void applyGeometry(); void onSampleArrived(); internal::CircularBuffer* m_line1 = nullptr; internal::CircularBuffer* m_line2 = nullptr; QColor m_line1Color; QColor m_line2Color; qreal m_line1FillAlpha = 0.15; qreal m_line2FillAlpha = 0.2; qreal m_maxValue = 1024.0; qreal m_lineWidth = 2.0; bool m_curveSmoothing = false; int m_historyLength = 30; bool m_sliding = true; qreal m_viewportW = 0.0; qreal m_viewportH = 0.0; // The height actually painted, snapped so the texture maps 1:1 onto device // rows. The plot bounds are built from this, not from the slot's height. qreal m_paintedH = 0.0; qreal m_dpr = 1.0; qreal m_slideIntervalMs = 0.0; // Restarted whenever a sample arrives, so the slide phase follows the sample // cadence instead of the animation's timeline. QElapsedTimer m_sampleClock; LineGeometry m_geom1; LineGeometry m_geom2; bool m_dirty = true; }; } // namespace ZShell::components