// SPDX-License-Identifier: GPL-2.0 #ifndef STATS_VIEW_H #define STATS_VIEW_H #include "statsstate.h" #include "statshelper.h" #include "statsselection.h" #include #include #include #include struct dive; struct StatsBinner; struct StatsBin; struct StatsState; struct StatsVariable; class StatsSeries; class CategoryAxis; class ChartItem; class ChartRectLineItem; class ChartTextItem; class CountAxis; class HistogramAxis; class HistogramMarker; class QuartileMarker; class RegressionItem; class StatsAxis; class StatsGrid; class StatsTheme; class Legend; class QSGTexture; class RootNode; // Internal implementation detail enum class ChartSubType : int; enum class ChartZValue : int; enum class StatsOperation : int; enum class ChartSortMode : int; class StatsView : public QQuickItem { Q_OBJECT public: StatsView(); StatsView(QQuickItem *parent); ~StatsView(); void plot(const StatsState &state); void updateFeatures(const StatsState &state); // Updates the visibility of chart features, such as legend, regression, etc. void restrictToSelection(); void unrestrict(); int restrictionCount() const; // <0: no restriction QQuickWindow *w() const; // Make window available to items QSizeF size() const; QRectF plotArea() const; void setTheme(bool dark); // Chart must be replot for theme to become effective. const StatsTheme &getCurrentTheme() const; void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread! void registerChartItem(ChartItem &item); void registerDirtyChartItem(ChartItem &item); void emergencyShutdown(); // Called when QQuick decides to delete our root node. // Create a chart item and add it to the scene. // The item must not be deleted by the caller, but can be // scheduled for deletion using deleteChartItem() below. // Most items can be made invisible, which is preferred over deletion. // All items on the scene will be deleted once the chart is reset. template ChartItemPtr createChartItem(Args&&... args); template void deleteChartItem(ChartItemPtr &item); private slots: void replotIfVisible(); void divesSelected(const QVector &dives); private: // QtQuick related things bool backgroundDirty; QRectF plotRect; QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override; #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; #else void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; #endif void plotAreaChanged(const QSizeF &size); void reset(); // clears all series and axes void setAxes(StatsAxis *x, StatsAxis *y); void plotBarChart(const std::vector &dives, ChartSubType subType, ChartSortMode sortMode, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable, const StatsBinner *valueBinner); void plotValueChart(const std::vector &dives, ChartSubType subType, ChartSortMode sortMode, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable, StatsOperation valueAxisOperation); void plotDiscreteCountChart(const std::vector &dives, ChartSubType subType, ChartSortMode sortMode, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner); void plotPieChart(const std::vector &dives, ChartSortMode sortMode, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner); void plotDiscreteBoxChart(const std::vector &dives, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable); void plotDiscreteScatter(const std::vector &dives, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable); void plotHistogramCountChart(const std::vector &dives, ChartSubType subType, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner); void plotHistogramValueChart(const std::vector &dives, ChartSubType subType, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable, StatsOperation valueAxisOperation); void plotHistogramStackedChart(const std::vector &dives, ChartSubType subType, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable, const StatsBinner *valueBinner); void plotHistogramBoxChart(const std::vector &dives, const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable); void plotScatter(const std::vector &dives, const StatsVariable *categoryVariable, const StatsVariable *valueVariable); void setTitle(const QString &); void updateTitlePos(); // After resizing, set title to correct position void plotChart(); void updateFeatures(); // Updates the visibility of chart features, such as legend, regression, etc. template T *createSeries(Args&&... args); template T *createAxis(const QString &title, Args&&... args); template CategoryAxis *createCategoryAxis(const QString &title, const StatsBinner &binner, const std::vector &bins, bool isHorizontal); template HistogramAxis *createHistogramAxis(const QString &title, const StatsBinner &binner, const std::vector &bins, bool isHorizontal); CountAxis *createCountAxis(int maxVal, bool isHorizontal); // Helper functions to add feature to the chart void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal); StatsState state; const StatsTheme *currentTheme; std::vector> series; std::unique_ptr grid; std::vector> quartileMarkers; ChartItemPtr medianMarker, meanMarker; StatsSeries *highlightedSeries; StatsAxis *xAxis, *yAxis; ChartItemPtr title; ChartItemPtr legend; Legend *draggedItem; ChartItemPtr regressionItem; ChartItemPtr selectionRect; QPointF dragStartMouse, dragStartItem; SelectionModifier selectionModifier; std::vector oldSelection; bool restrictDives; std::vector restrictedDives; // sorted by pointer for quick lookup. void hoverEnterEvent(QHoverEvent *event) override; void hoverMoveEvent(QHoverEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; RootNode *rootNode; // There are three double linked lists of chart items: // clean items, dirty items and items to be deleted. // Note that only the render thread must delete chart items, // and therefore these lists are the only owning pointers // to chart items. All other pointers are non-owning and // can therefore become stale. struct ChartItemList { ChartItemList(); ChartItem *first, *last; void append(ChartItem &item); void remove(ChartItem &item); void clear(); void splice(ChartItemList &list); }; ChartItemList cleanItems, dirtyItems, deletedItems; void deleteChartItemInternal(ChartItem &item); void freeDeletedChartItems(); }; // This implementation detail must be known to users of the class. // Perhaps move it into a statsview_impl.h file. template ChartItemPtr StatsView::createChartItem(Args&&... args) { return ChartItemPtr(new T(*this, std::forward(args)...)); } template void StatsView::deleteChartItem(ChartItemPtr &item) { deleteChartItemInternal(*item); item.reset(); } #endif