Skip to content

File PlotView.hpp

File List > QAccelPlot > src > QAccelPlot > PlotView.hpp

Go to the documentation of this file

//
// SPDX-FileCopyrightText: 2026 Michal Grabarczyk
// SPDX-License-Identifier: GPL-3.0-only WITH Universal-FOSS-exception-1.0
//
// This file is also available under a separate commercial license.
// See COMMERCIAL-LICENSING.md for contact information.
//
#pragma once

#include "QAccelPlot/Border.hpp"
#include "QAccelPlot/PlotDragRect.hpp"
#include "QAccelPlot/PlotMouseEvent.hpp"
#include "QAccelPlot/PlotRectangleZoom.hpp"
#include "QAccelPlot/axis/Axis.hpp"
#include "QAccelPlot/grid/Grid.hpp"
#include "QAccelPlot/series/PlotSeries.hpp"
#include "QAccelPlot/theme/ColorPalette.hpp"

#include <QColor>
#include <QKeyEvent>
#include <QPointF>
#include <QQmlListProperty>
#include <QQuickItem>
#include <QRectF>

namespace QAccelPlot {

class GridNode;
class PlotExporter;
class PlotInspector;

namespace Internal {
class PointerTracker;
class RectangleZoomOverlay;
} // namespace Internal

class PlotView : public QQuickItem {
    Q_OBJECT
    QML_NAMED_ELEMENT(PlotView)


    Q_PROPERTY(Axis* xAxis READ xAxis WRITE setXAxis NOTIFY xAxisChanged)
    Q_PROPERTY(Axis* yAxis READ yAxis WRITE setYAxis NOTIFY yAxisChanged)
    Q_PROPERTY(Axis* x2Axis READ x2Axis WRITE setX2Axis NOTIFY x2AxisChanged)
    Q_PROPERTY(Axis* y2Axis READ y2Axis WRITE setY2Axis NOTIFY y2AxisChanged)
    Q_PROPERTY(QQmlListProperty<Axis> extraAxes READ extraAxes)
    Q_PROPERTY(qreal padding READ padding WRITE setPadding NOTIFY paddingChanged)
    Q_PROPERTY(QRectF plotRect READ plotRect NOTIFY plotRectChanged)
    Q_PROPERTY(QColor plotAreaColor READ plotAreaColor WRITE setPlotAreaColor NOTIFY plotAreaColorChanged)
    Q_PROPERTY(QColor axesAreaColor READ axesAreaColor WRITE setAxesAreaColor NOTIFY axesAreaColorChanged)
    Q_PROPERTY(Border* border READ border CONSTANT)
    Q_PROPERTY(PlotRectangleZoom* rectangleZoom READ rectangleZoom CONSTANT)
    Q_PROPERTY(Grid* grid READ grid CONSTANT)
    Q_PROPERTY(QList<PlotSeries*> series READ series NOTIFY seriesChanged)
    Q_PROPERTY(QQuickItem* overlay READ overlay CONSTANT)

public:
    explicit PlotView(QQuickItem* parent = nullptr);
    ~PlotView() override;

    Q_INVOKABLE qreal dataToPixelX(qreal dataValue) const;
    Q_INVOKABLE qreal dataToPixelY(qreal dataValue) const;
    Q_INVOKABLE qreal pixelToDataX(qreal pixelX) const;
    Q_INVOKABLE qreal pixelToDataY(qreal pixelY) const;
    Q_INVOKABLE bool isInsidePlotArea(qreal x, qreal y) const;
    Q_INVOKABLE bool zoomToRect(const QRectF& rect);
    Q_INVOKABLE void rescaleAllAxes();

    Axis* xAxis() const;
    void setXAxis(Axis* axis);

    Axis* yAxis() const;
    void setYAxis(Axis* axis);

    Axis* x2Axis() const;
    void setX2Axis(Axis* axis);

    Axis* y2Axis() const;
    void setY2Axis(Axis* axis);

    QQmlListProperty<Axis> extraAxes();

    qreal padding() const;
    void setPadding(qreal p);

    QRectF plotRect() const;

    QColor plotAreaColor() const;
    void setPlotAreaColor(const QColor& c);

    QColor axesAreaColor() const;
    void setAxesAreaColor(const QColor& c);

    Border* border() const;

    PlotRectangleZoom* rectangleZoom() const;

    Grid* grid() const;

    QList<PlotSeries*> series() const;

    QQuickItem* overlay() const;

signals:
    void escapePressed();
    void pointerGrabLost();
    void xAxisChanged();
    void yAxisChanged();
    void x2AxisChanged();
    void y2AxisChanged();
    void paddingChanged();
    void plotRectChanged();
    void plotAreaColorChanged();
    void axesAreaColorChanged();
    void seriesChanged();
    void mousePressed(PlotMouseEvent* event);
    void mouseReleased(PlotMouseEvent* event);
    void mouseDoubleClicked(PlotMouseEvent* event);
    void mouseMoved(PlotMouseEvent* event);

protected:
    void wheelEvent(QWheelEvent* event) override;
    void mousePressEvent(QMouseEvent* event) override;
    void hoverEnterEvent(QHoverEvent* event) override;
    void hoverMoveEvent(QHoverEvent* event) override;
    void hoverLeaveEvent(QHoverEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void mouseReleaseEvent(QMouseEvent* event) override;
    void mouseUngrabEvent() override;
    void focusOutEvent(QFocusEvent* event) override;
    void mouseDoubleClickEvent(QMouseEvent* event) override;
    void keyPressEvent(QKeyEvent* event) override;
    void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; 
    void itemChange(ItemChange change, const ItemChangeData& value) override;
    QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;

private:
    friend class PlotExporter;
    friend class PlotInspector;

    enum class DragMode { Idle, Pan, RectangleZoom };
    bool isAxisRegistered(Axis* axis) const;
    static void appendExtraAxis(QQmlListProperty<Axis>* list, Axis* axis);
    static qsizetype extraAxisCount(QQmlListProperty<Axis>* list);
    static Axis* extraAxis(QQmlListProperty<Axis>* list, qsizetype index);
    static void clearExtraAxes(QQmlListProperty<Axis>* list);

    bool tryForwardKeyEventToAxis(Axis* axis, const QPointF& mousePos, QKeyEvent* event);
    bool tryZoomAxisAtPosition(Axis* axis, const QPointF& pos, qreal wheelSteps);
    void zoomAxisAtRatio(Axis* axis, qreal ratio, qreal wheelSteps);
    void connectAxisSignals(Axis* axis);
    void disconnectAxisSignals(Axis* axis);
    void axisDestroyed(QObject* object);
    void connectAxis(Axis* axis, Axis::Side side);
    void disconnectAxis(Axis* axis);
    void zoomAxis(Axis* axis, qreal factor, qreal centerRatio);
    void panAxis(Axis* axis, qreal delta, qreal length);
    QList<Axis*> attachedAxes() const;
    void updateRectangleSelection(const QPointF& pos);
    void cancelRectangleSelection();
    void layoutAxes();
    // Returns the pointer over the plot, for PlotInspector.
    const Internal::PointerTracker& pointer() const;
    void registerSeries(QQuickItem* item);
    // Marks the plot as captured by a PlotExporter, drawing both background areas transparent when \a transparentBackground.
    void beginExport(bool transparentBackground);
    // Ends the capture and restores the backgrounds.
    void endExport();

    Axis* xAxis_{nullptr};
    Axis* yAxis_{nullptr};
    Axis* x2Axis_{nullptr};
    Axis* y2Axis_{nullptr};
    QList<Axis*> extraAxes_;

    DragMode dragMode_{DragMode::Idle};
    PlotDragRect zoomDrag_;
    QPointF lastMousePos_;
    // Key events go to the axis under the pointer.
    Internal::PointerTracker* pointer_{nullptr};
    PlotMouseEvent mousePressEvent_{this};
    PlotMouseEvent mouseReleaseEvent_{this};
    PlotMouseEvent mouseDoubleClickEvent_{this};
    PlotMouseEvent mouseMoveEvent_{this};
    qreal padding_{24.0};
    QRectF plotRect_;
    QColor plotAreaColor_{ColorPalette::dark().plotArea};
    QColor axesAreaColor_{ColorPalette::dark().axesArea};
    // One exporter captures the plot at a time: the capture changes how the plot renders.
    bool exporting_{false};
    bool transparentBackground_{false};
    Border* border_{nullptr};
    PlotRectangleZoom* rectangleZoom_{nullptr};
    Internal::RectangleZoomOverlay* rectangleZoomOverlay_{nullptr};
    QQuickItem* overlay_{nullptr};
    Grid* grid_{nullptr};
    GridNode* gridNode_{nullptr};
    QList<PlotSeries*> series_;
};

} // namespace QAccelPlot