Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include "graphs2d/qabstractseries.h"
++ #include "graphs2d/qabstractseries_p.h"
++ #if QT_CONFIG(graphs_2d_area)
++ #include <QtGraphs/qareaseries.h>
++ #include <private/arearenderer_p.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_bar)
++ #include <QtGraphs/qbarseries.h>
++ #include <private/barsrenderer_p.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ #include <QtGraphs/qpieseries.h>
++ #include <private/pierenderer_p.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_area)
++ #include <QtGraphs/qlineseries.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_scatter)
++ #include <QtGraphs/qscatterseries.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_spline)
++ #include <QtGraphs/qsplineseries.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ #include <private/pointrenderer_p.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_custom)
++ #include <QtGraphs/qcustomseries.h>
++ #include <private/customrenderer_p.h>
++ #endif
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ #include "qcpainteritem_p.h"
++ #endif
++ #include <QTimer>
++ #include <QtQuick/private/qquickpinchhandler_p.h>
++ #include <QtQuick/private/qquickrectangle_p.h>
++ #include <QtCore/qset.h>
++ #include <private/axisrenderer_p.h>
++ #include <private/qabstractaxis_p.h>
++ #include <private/qgraphsview_p.h>
++
++ #include <qtgraphs_tracepoints_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ Q_TRACE_PREFIX(qtgraphs,
++ "QT_BEGIN_NAMESPACE" \
++ "class QGraphsView;" \
++ "QT_END_NAMESPACE"
++ )
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsVieUpdatePolish_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsVieUpdatePolish_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewComponentComplete_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewComponentComplete_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewInsertSeries_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewInsertSeries_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreateBarsRenderer_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreateBarsRenderer_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreateAxisRenderer_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreateAxisRenderer_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreatePointRenderer_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreatePointRenderer_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreatePieRenderer_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreatePieRenderer_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreateAreaRenderer_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs2DGraphsViewCreateAreaRenderer_exit);
++
++ /*!
++ \class QGraphsView
++ \inmodule QtGraphs
++ \internal
++ */
++
++ /*!
++ \qmltype GraphsView
++ \nativetype QGraphsView
++ \inqmlmodule QtGraphs
++ \ingroup graphs_qml_2D
++ \brief Base type for all Qt Graphs views.
++
++ This class collects the series and theming together and draws the graphs.
++ You will need to import Qt Graphs module to use this type:
++
++ \snippet doc_src_qmlgraphs.cpp 0
++
++ After that you can use GraphsView in your qml files:
++
++ \snippet doc_src_qmlgraphs.cpp 10
++
++ \image graphsview-minimal.png {Bar graph displaying values for 2023, 2024, and 2025}
++
++ \sa BarSeries, LineSeries, BarCategoryAxis, ValueAxis, GraphsTheme
++ */
++
++ Q_LOGGING_CATEGORY(lcGraphs2D, "qt.graphs2d.general")
++ Q_LOGGING_CATEGORY(lcViewProperties2D, "qt.graphs2d.graphsview.properties")
++ Q_LOGGING_CATEGORY(lcEvents2D, "qt.graphs2d.events")
++ Q_LOGGING_CATEGORY(lcCritical2D, "qt.graphs2d.critical")
++
++ QGraphsView::QGraphsView(QQuickItem *parent) :
++ QQuickItem(parent)
++ {
++ setFlag(QQuickItem::ItemHasContents);
++ setAcceptedMouseButtons(Qt::LeftButton);
++ setAcceptHoverEvents(true);
++ m_defaultTheme = new QGraphsTheme(this);
++ m_pinchHandler = new QQuickPinchHandler(this);
++ m_pinchHandler->setTarget(nullptr);
++
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ if (m_useCanvasPainter)
++ createCanvasPainter();
++ #endif
++
++ QObject::connect(m_pinchHandler,
++ &QQuickPinchHandler::scaleChanged,
++ this,
++ &QGraphsView::onPinchScaleChanged);
++ QObject::connect(m_pinchHandler,
++ &QQuickPinchHandler::grabChanged,
++ this,
++ &QGraphsView::onPinchGrabChanged);
++ m_initialized = true;
++ }
++
++ QGraphsView::~QGraphsView()
++ {
++ const auto slist = m_seriesList;
++ for (const auto &s : slist) {
++ if (auto series = qobject_cast<QAbstractSeries *>(s))
++ detachSeries(series);
++ }
++ if (m_axisX)
++ m_axisX->d_func()->setGraph(nullptr);
++ if (m_axisY)
++ m_axisY->d_func()->setGraph(nullptr);
++ }
++
++ void QGraphsView::onPinchScaleChanged(qreal delta)
++ {
++ if (m_axisRenderer)
++ m_axisRenderer->handlePinchScale(delta);
++ }
++
++ void QGraphsView::onPinchGrabChanged(QPointingDevice::GrabTransition transition, QEventPoint point)
++ {
++ if (m_axisRenderer)
++ m_axisRenderer->handlePinchGrab(transition, point);
++ }
++
++ /*!
++ \qmlmethod void GraphsView::addSeries(AbstractSeries series)
++ Appends a \a series into GraphsView.
++ If the \a series is null, it will not be added. If the \a series already
++ belongs to the graph, it will be moved into the end.
++ */
++ /*!
++ Appends a \a series into GraphsView.
++ If the \a series is null, it will not be added. If the \a series already
++ belongs to the graph, it will be moved into the end.
++ */
++ void QGraphsView::addSeries(QObject *series)
++ {
++ insertSeries(m_seriesList.size(), series);
++ }
++
++ /*!
++ \qmlmethod void GraphsView::insertSeries(int index, AbstractSeries series)
++ Inserts a \a series at the position specified by \a index.
++ If the \a series is null, it will not be inserted. If the \a series already
++ belongs to the graph, it will be moved into \a index.
++ */
++ /*!
++ Inserts a \a series at the position specified by \a index.
++ If the \a series is null, it will not be inserted. If the \a series already
++ belongs to the graph, it will be moved into \a index.
++ */
++ void QGraphsView::insertSeries(qsizetype index, QObject *object)
++ {
++ if (!isSeriesSupported(object))
++ return;
++
++ if (auto series = qobject_cast<QAbstractSeries *>(object)) {
++ Q_TRACE(QGraphs2DGraphsViewInsertSeries_entry);
++ series->setGraph(this);
++ if (m_seriesList.contains(series)) {
++ qsizetype oldIndex = m_seriesList.indexOf(series);
++ if (index != oldIndex) {
++ m_seriesList.removeOne(series);
++ if (oldIndex < index)
++ index--;
++ m_seriesList.insert(index, series);
++ qCDebug(lcGraphs2D, "series was already in seriesList, removed old series at index: %" PRIdQSIZETYPE
++ " and inserted new one at index: %" PRIdQSIZETYPE,
++ oldIndex, index);
++ }
++ } else {
++ m_seriesList.insert(index, series);
++
++ QObject::connect(series,
++ &QAbstractSeries::update,
++ this,
++ &QGraphsView::updateComponentSizes);
++ QObject::connect(series, &QAbstractSeries::update,
++ this, &QGraphsView::polishAndUpdate);
++ QObject::connect(series, &QAbstractSeries::hoverEnter,
++ this, &QGraphsView::handleHoverEnter);
++ QObject::connect(series, &QAbstractSeries::hoverExit,
++ this, &QGraphsView::handleHoverExit);
++ QObject::connect(series, &QAbstractSeries::hover,
++ this, &QGraphsView::handleHover);
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (auto pie = qobject_cast<QPieSeries *>(series))
++ connect(pie, &QPieSeries::removed, m_pieRenderer, &PieRenderer::markedDeleted);
++ #endif
++ #if QT_CONFIG(graphs_2d_bar)
++ if (auto bar = qobject_cast<QBarSeries *>(series); bar && m_barsRenderer) {
++ connect(bar,
++ &QBarSeries::barsetsRemoved,
++ m_barsRenderer,
++ &BarsRenderer::handleBarSetsRemoved);
++ }
++ #endif
++ qCDebug(lcGraphs2D) << series << "added to a list at index of" << index;
++ }
++ Q_TRACE(QGraphs2DGraphsViewInsertSeries_exit);
++
++ updateComponentSizes();
++ polishAndUpdate();
++ }
++ }
++
++ /*!
++ \qmlmethod void GraphsView::removeSeries(AbstractSeries series)
++ Removes the \a series from the graph.
++ */
++ /*!
++ Removes the \a series from the graph.
++ */
++ void QGraphsView::removeSeries(QObject *object)
++ {
++ if (auto series = qobject_cast<QAbstractSeries *>(object)) {
++ detachSeries(series);
++ updateComponentSizes();
++ polishAndUpdate();
++ }
++ }
++
++ void QGraphsView::detachSeries(QAbstractSeries *series)
++ {
++ series->setGraph(nullptr);
++ m_seriesList.removeAll(series);
++ auto &cleanupSeriesList = m_cleanupSeriesList[getSeriesRendererIndex(series)];
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (auto pie = qobject_cast<QPieSeries *>(series))
++ disconnect(pie, &QPieSeries::removed, m_pieRenderer, &PieRenderer::markedDeleted);
++ #endif
++ #if QT_CONFIG(graphs_2d_bar)
++ if (auto bar = qobject_cast<QBarSeries *>(series); bar && m_barsRenderer) {
++ disconnect(bar,
++ &QBarSeries::barsetsRemoved,
++ m_barsRenderer,
++ &BarsRenderer::handleBarSetsRemoved);
++ }
++ #endif
++ qCDebug(lcGraphs2D) << "removing" << series << "from seriesList";
++ cleanupSeriesList.append(series);
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer)
++ m_pointRenderer->seriesAboutToBeRemoved(series);
++ #endif
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer)
++ m_barsRenderer->seriesAboutToBeRemoved(series);
++ #endif
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer)
++ m_pieRenderer->seriesAboutToBeRemoved(series);
++ #endif
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer)
++ m_areaRenderer->seriesAboutToBeRemoved(series);
++ #endif
++ #if QT_CONFIG(graphs_2d_custom)
++ if (m_customRenderer)
++ m_customRenderer->seriesAboutToBeRemoved(series);
++ #endif
++ }
++
++ /*!
++ \qmlmethod void GraphsView::removeSeries(int index)
++ Removes the series specified by \a index from the graph.
++ */
++ /*!
++ Removes the series specified by \a index from the graph.
++ */
++ void QGraphsView::removeSeries(qsizetype index)
++ {
++ if (index >= 0 && index < m_seriesList.size())
++ removeSeries(m_seriesList[index]);
++ }
++
++ /*!
++ \qmlmethod bool GraphsView::hasSeries(AbstractSeries series)
++ Returns \c true if the \a series is in the graph.
++ */
++ /*!
++ Returns \c true if the \a series is in the graph.
++ */
++ bool QGraphsView::hasSeries(QObject *series)
++ {
++ return m_seriesList.contains(series);
++ }
++
++ QPointF QGraphsView::getDataPointCoordinates(QAbstractSeries *series, qreal x, qreal y)
++ {
++ #if QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_area)
++ if (m_pointRenderer)
++ return m_pointRenderer->reverseRenderCoordinates(series, x, y);
++ #else
++ Q_UNUSED(series);
++ Q_UNUSED(x);
++ Q_UNUSED(y);
++ #endif
++ return QPointF();
++ }
++
++ bool QGraphsView::isSeriesSupported(QObject *series) const
++ {
++ Q_UNUSED(series);
++ return true;
++ }
++
++ bool QGraphsView::addAxis(QAbstractAxis *axis)
++ {
++ if (axis) {
++ axis->d_func()->setGraph(this);
++ // Ensure AxisRenderer exists
++ createAxisRenderer();
++ polishAndUpdate();
++ QObject::connect(axis, &QAbstractAxis::update, this, &QGraphsView::polishAndUpdate);
++ QObject::connect(axis, &QAbstractAxis::update, this, &QGraphsView::updateComponentSizes);
++ return true;
++ }
++ return false;
++ }
++
++ void QGraphsView::removeAxis(QAbstractAxis *axis, bool removeAllReferences)
++ {
++ if (axis) {
++ axis->d_func()->setGraph(nullptr);
++ QObject::disconnect(axis, &QAbstractAxis::update, this, &QGraphsView::polishAndUpdate);
++ QObject::disconnect(axis,
++ &QAbstractAxis::update,
++ this,
++ &QGraphsView::updateComponentSizes);
++ }
++
++ if (m_axisX == axis)
++ m_axisX = nullptr;
++ if (m_axisY == axis)
++ m_axisY = nullptr;
++
++ if (removeAllReferences) {
++ for (auto&& s : m_seriesList) {
++ if (auto series = qobject_cast<QAbstractSeries *>(s))
++ series->d_func()->removeAxis(axis);
++ }
++ }
++
++ updateComponentSizes();
++ polishAndUpdate();
++ }
++
++ qsizetype QGraphsView::graphSeriesCount() const
++ {
++ return m_graphSeriesCount;
++ }
++
++ void QGraphsView::setGraphSeriesCount(qsizetype count)
++ {
++ if (count > m_graphSeriesCount)
++ m_graphSeriesCount = count;
++ }
++
++ #if QT_CONFIG(graphs_2d_bar)
++ void QGraphsView::createBarsRenderer()
++ {
++ Q_TRACE_SCOPE(QGraphs2DGraphsViewCreateBarsRenderer);
++ if (!m_barsRenderer) {
++ qCDebug(lcGraphs2D, "creating bars renderer");
++ m_barsRenderer = new BarsRenderer(this, clipPlotArea());
++ updateComponentSizes();
++ }
++ }
++ #endif
++
++ void QGraphsView::createAxisRenderer()
++ {
++ Q_TRACE_SCOPE(QGraphs2DGraphsViewCreateAxisRenderer);
++ if (!m_axisRenderer) {
++ qCDebug(lcGraphs2D) << "creating axis renderer.";
++ m_axisRenderer = new AxisRenderer(this);
++ m_axisRenderer->setZ(-1);
++ updateComponentSizes();
++ }
++ }
++
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ void QGraphsView::createPointRenderer()
++ {
++ Q_TRACE_SCOPE(QGraphs2DGraphsViewCreatePointRenderer);
++ if (!m_pointRenderer) {
++ qCDebug(lcGraphs2D, "creating point renderer.");
++ m_pointRenderer = new PointRenderer(this, clipPlotArea());
++ updateComponentSizes();
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ void QGraphsView::createPieRenderer()
++ {
++ Q_TRACE_SCOPE(QGraphs2DGraphsViewCreatePieRenderer);
++ if (!m_pieRenderer) {
++ qCDebug(lcGraphs2D, "creating pie renderer.");
++ m_pieRenderer = new PieRenderer(this, clipPlotArea());
++ updateComponentSizes();
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area)
++ void QGraphsView::createAreaRenderer()
++ {
++ Q_TRACE_SCOPE(QGraphs2DGraphsViewCreateAreaRenderer);
++ if (!m_areaRenderer) {
++ qCDebug(lcGraphs2D, "creating area renderer.");
++ m_areaRenderer = new AreaRenderer(this, clipPlotArea());
++ updateComponentSizes();
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_custom)
++ void QGraphsView::createCustomRenderer()
++ {
++ if (!m_customRenderer) {
++ qCDebug(lcGraphs2D, "creating custom renderer.");
++ m_customRenderer = new CustomRenderer(this, clipPlotArea());
++ updateComponentSizes();
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ void QGraphsView::createCanvasPainter()
++ {
++ if (!m_painterItem) {
++ qCDebug(lcGraphs2D, "creating canvas painter item.");
++ m_painterItem = new QCPainterItem(m_areaRenderer, m_barsRenderer, m_pieRenderer,
++ m_pointRenderer, m_customRenderer, this);
++ m_painterItem->setAlphaBlending(true);
++ m_painterItem->setFillColor(Qt::transparent);
++ m_painterItem->setAntialiasing(true);
++
++ if (m_initialized)
++ updateComponentSizes();
++ }
++ }
++
++ void QGraphsView::removeCanvasPainter()
++ {
++ if (m_painterItem) {
++ m_painterItem->deleteLater();
++ m_painterItem = nullptr;
++ }
++ }
++ #endif
++
++ /*!
++ \property QGraphsView::axisXSmoothing
++ \brief Controls the graph X axis smoothing (antialiasing) amount.
++ By default, the smoothing is \c 1.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::axisXSmoothing
++ Controls the graph X axis smoothing (antialiasing) amount.
++ By default, the smoothing is \c 1.0.
++ */
++ qreal QGraphsView::axisXSmoothing() const
++ {
++ return m_axisXSmoothing;
++ }
++
++ void QGraphsView::setAxisXSmoothing(qreal smoothing)
++ {
++ if (QtPrivate::fuzzyCompare(m_axisXSmoothing, smoothing)) {
++ qCDebug(lcViewProperties2D, "%s axis smoothing is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)),
++ smoothing);
++ return;
++ }
++ m_axisXSmoothing = smoothing;
++ emit axisXSmoothingChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::axisYSmoothing
++ \brief Controls the graph Y axis smoothing (antialiasing) amount.
++ By default, the smoothing is \c 1.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::axisYSmoothing
++ Controls the graph Y axis smoothing (antialiasing) amount.
++ By default, the smoothing is \c 1.0.
++ */
++ qreal QGraphsView::axisYSmoothing() const
++ {
++ return m_axisYSmoothing;
++ }
++
++ void QGraphsView::setAxisYSmoothing(qreal smoothing)
++ {
++ if (QtPrivate::fuzzyCompare(m_axisYSmoothing, smoothing)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), smoothing);
++ return;
++ }
++ m_axisYSmoothing = smoothing;
++ emit axisYSmoothingChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::gridSmoothing
++ \brief Controls the graph grid smoothing (antialiasing) amount.
++ By default, the smoothing is \c 1.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::gridSmoothing
++ Controls the graph grid smoothing (antialiasing) amount.
++ By default, the smoothing is \c 1.0.
++ */
++ qreal QGraphsView::gridSmoothing() const
++ {
++ return m_gridSmoothing;
++ }
++
++ void QGraphsView::setGridSmoothing(qreal smoothing)
++ {
++ if (QtPrivate::fuzzyCompare(m_gridSmoothing, smoothing)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), smoothing);
++ return;
++ }
++ m_gridSmoothing = smoothing;
++ emit gridSmoothingChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::shadowVisible
++ \brief Controls if the graph grid shadow is visible.
++ By default, shadow visibility is set to \c false.
++ */
++ /*!
++ \qmlproperty bool GraphsView::shadowVisible
++ Controls if the graph grid shadow is visible.
++ By default, shadow visibility is set to \c false.
++ */
++ bool QGraphsView::isShadowVisible() const
++ {
++ return m_isShadowVisible;
++ }
++
++ void QGraphsView::setShadowVisible(bool newShadowVisibility)
++ {
++ if (m_isShadowVisible == newShadowVisibility) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newShadowVisibility;
++ return;
++ }
++ m_isShadowVisible = newShadowVisibility;
++ emit shadowVisibleChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::shadowColor
++ \brief Controls the graph grid shadow color.
++ By default, shadow color is set to \c black.
++ */
++ /*!
++ \qmlproperty color GraphsView::shadowColor
++ Controls the graph grid shadow color.
++ By default, shadow color is set to \c black.
++ */
++ QColor QGraphsView::shadowColor() const
++ {
++ return m_shadowColor;
++ }
++
++ void QGraphsView::setShadowColor(QColor newShadowColor)
++ {
++ if (m_shadowColor == newShadowColor) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %s",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), qUtf8Printable(newShadowColor.name()));
++ return;
++ }
++ m_shadowColor = newShadowColor;
++ emit shadowColorChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::shadowBarWidth
++ \brief Controls the graph grid shadow width.
++ By default, shadow width is set to \c 2.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::shadowBarWidth
++ Controls the graph grid shadow width.
++ By default, shadow width is set to \c 2.0.
++ */
++ qreal QGraphsView::shadowBarWidth() const
++ {
++ return m_shadowBarWidth;
++ }
++
++ void QGraphsView::setShadowBarWidth(qreal newShadowBarWidth)
++ {
++ if (QtPrivate::fuzzyCompare(m_shadowBarWidth, newShadowBarWidth)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newShadowBarWidth);
++ return;
++ }
++ m_shadowBarWidth = newShadowBarWidth;
++ emit shadowBarWidthChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::shadowXOffset
++ \brief Controls the graph grid shadow X offset.
++ By default, shadow X offset is set to \c 0.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::shadowXOffset
++ Controls the graph grid shadow X offset.
++ By default, shadow X offset is set to \c 0.0.
++ */
++ qreal QGraphsView::shadowXOffset() const
++ {
++ return m_shadowXOffset;
++ }
++
++ void QGraphsView::setShadowXOffset(qreal newShadowXOffset)
++ {
++ if (QtPrivate::fuzzyCompare(m_shadowXOffset, newShadowXOffset)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newShadowXOffset);
++ return;
++ }
++ m_shadowXOffset = newShadowXOffset;
++ emit shadowXOffsetChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::shadowYOffset
++ \brief Controls the graph grid shadow Y offset.
++ By default, shadow Y offset is set to \c 0.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::shadowYOffset
++ Controls the graph grid shadow Y offset.
++ By default, shadow Y offset is set to \c 0.0.
++ */
++ qreal QGraphsView::shadowYOffset() const
++ {
++ return m_shadowYOffset;
++ }
++
++ void QGraphsView::setShadowYOffset(qreal newShadowYOffset)
++ {
++ if (QtPrivate::fuzzyCompare(m_shadowYOffset, newShadowYOffset)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newShadowYOffset);
++ return;
++ }
++ m_shadowYOffset = newShadowYOffset;
++ emit shadowYOffsetChanged();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::shadowSmoothing
++ \brief Controls the graph grid shadow smoothing (antialiasing) amount.
++ By default, shadow smoothing is set to \c 4.0.
++ */
++ /*!
++ \qmlproperty real GraphsView::shadowSmoothing
++ Controls the graph grid shadow smoothing (antialiasing) amount.
++ By default, shadow smoothing is set to \c 4.0.
++ */
++ qreal QGraphsView::shadowSmoothing() const
++ {
++ return m_shadowSmoothing;
++ }
++
++ void QGraphsView::setShadowSmoothing(qreal smoothing)
++ {
++ if (QtPrivate::fuzzyCompare(m_shadowSmoothing, smoothing)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), smoothing);
++ return;
++ }
++ m_shadowSmoothing = smoothing;
++ emit shadowSmoothingChanged();
++ polishAndUpdate();
++ }
++
++ void QGraphsView::handleHoverEnter(const QString &seriesName, QPointF position, QPointF value)
++ {
++ if (m_hoverCount == 0)
++ emit hoverEnter(seriesName, position, value);
++ m_hoverCount++;
++ }
++
++ void QGraphsView::handleHoverExit(const QString &seriesName, QPointF position)
++ {
++ m_hoverCount--;
++ if (m_hoverCount == 0)
++ emit hoverExit(seriesName, position);
++ }
++
++ void QGraphsView::handleHover(const QString &seriesName, QPointF position, QPointF value)
++ {
++ emit hover(seriesName, position, value);
++ }
++
++ void QGraphsView::updateComponentSizes()
++ {
++ qCDebug(lcEvents2D, "updating component sizes.");
++
++ if (m_axisRenderer)
++ m_axisRenderer->updateAxisMeasurements();
++
++ updateAxisAreas();
++ updatePlotArea();
++
++ if (m_axisRenderer)
++ m_axisRenderer->setSize(size());
++
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer) {
++ m_barsRenderer->setX(m_plotArea.x());
++ m_barsRenderer->setY(m_plotArea.y());
++ m_barsRenderer->setSize(m_plotArea.size());
++ qCDebug(lcEvents2D) << "bars graph size:" << m_plotArea.size();
++ qCDebug(lcEvents2D, "barsRenderer plotArea x: %f y: %f",
++ m_plotArea.x(),
++ m_plotArea.y());
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer) {
++ m_pointRenderer->setX(m_plotArea.x());
++ m_pointRenderer->setY(m_plotArea.y());
++ m_pointRenderer->setSize(m_plotArea.size());
++ qCDebug(lcEvents2D) << "point graph size:" << m_plotArea.size();
++ qCDebug(lcEvents2D, "pointRenderer plotArea x: %f y: %f",
++ m_plotArea.x(),
++ m_plotArea.y());
++
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer) {
++ m_pieRenderer->setX(m_plotArea.x());
++ m_pieRenderer->setY(m_plotArea.y());
++ m_pieRenderer->setSize(m_plotArea.size());
++ qCDebug(lcEvents2D) << "pie graph size:" << m_plotArea.size();
++ qCDebug(lcEvents2D, "pieRenderer plotArea x: %f y: %f",
++ m_plotArea.x(),
++ m_plotArea.y());
++
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer) {
++ m_areaRenderer->setX(m_plotArea.x());
++ m_areaRenderer->setY(m_plotArea.y());
++ m_areaRenderer->setSize(m_plotArea.size());
++ qCDebug(lcEvents2D) << "area graph size:" << m_plotArea.size();
++ qCDebug(lcEvents2D, "areaRenderer plotArea x: %f y: %f",
++ m_plotArea.x(),
++ m_plotArea.y());
++
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_custom)
++ if (m_customRenderer) {
++ m_customRenderer->setX(m_plotArea.x());
++ m_customRenderer->setY(m_plotArea.y());
++ m_customRenderer->setSize(m_plotArea.size());
++ qCDebug(lcEvents2D) << "custom graph size:" << m_plotArea.size();
++ qCDebug(lcEvents2D, "customaRenderer plotArea x: %f y: %f", m_plotArea.x(), m_plotArea.y());
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ if (m_painterItem) {
++ m_painterItem->setX(m_plotArea.x());
++ m_painterItem->setY(m_plotArea.y());
++ m_painterItem->setSize(m_plotArea.size());
++ }
++ #endif
++ }
++
++ void QGraphsView::componentComplete()
++ {
++ Q_TRACE(QGraphs2DGraphsViewComponentComplete_entry);
++ if (!m_zoomAreaDelegate && !m_zoomAreaItem) {
++
++ QQmlComponent tempZoomAreaDelegate(qmlEngine(this));
++ tempZoomAreaDelegate.loadFromModule(u"QtGraphs2D.impl"_s, u"ZoomAreaDelegate"_s);
++
++ m_zoomAreaItem = qobject_cast<QQuickItem *>(
++ tempZoomAreaDelegate.create(tempZoomAreaDelegate.creationContext()));
++ m_zoomAreaItem->setParent(this);
++ m_zoomAreaItem->setParentItem(this);
++ m_zoomAreaItem->setVisible(false);
++ }
++
++ if (!m_theme) {
++ m_theme = m_defaultTheme;
++ QObject::connect(m_theme, &QGraphsTheme::update, this, &QQuickItem::update);
++ QObject::connect(m_theme, &QGraphsTheme::update,
++ this, &QGraphsView::updateComponentSizes);
++ m_theme->resetColorTheme();
++ // resetColorTheme() only emits QGraphsTheme::update (and thus triggers
++ // updateComponentSizes()) if the theme actually changes, which it doesn't
++ // here since QtGreen is already the default. Recompute axis margins
++ // explicitly so they don't stay at their stale, theme-less construction-time
++ // values for the first frame.
++ if (m_axisRenderer)
++ updateComponentSizes();
++ }
++ Q_TRACE(QGraphs2DGraphsViewComponentComplete_exit);
++
++ QQuickItem::componentComplete();
++
++ qCDebug(lcEvents2D, "QGraphsView::componentComplete.");
++
++ ensurePolished();
++ }
++
++ void QGraphsView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
++ {
++ qCDebug(lcEvents2D) << "QGraphsView::geometryChange."
++ << "oldGeometry:" << oldGeometry
++ << "newGeometry:" << newGeometry;
++
++ QQuickItem::geometryChange(newGeometry, oldGeometry);
++
++ updateComponentSizes();
++
++ ensurePolished();
++ }
++
++ void QGraphsView::hoverMoveEvent(QHoverEvent *event)
++ {
++ bool handled = false;
++
++ // Adjust event position to renderers position
++ QPointF localPos = event->position() - m_plotArea.topLeft();
++ QHoverEvent mappedEvent(event->type(), localPos,event->globalPosition(),
++ event->oldPosF(), event->modifiers());
++ mappedEvent.setAccepted(false);
++
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer)
++ handled |= m_barsRenderer->handleHoverMove(&mappedEvent);
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer)
++ handled |= m_pointRenderer->handleHoverMove(&mappedEvent);
++ #endif
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer)
++ handled |= m_pieRenderer->handleHoverMove(&mappedEvent);
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer)
++ handled |= m_areaRenderer->handleHoverMove(&mappedEvent);
++ #endif
++
++ if (!handled)
++ event->ignore();
++ }
++
++ void QGraphsView::wheelEvent(QWheelEvent *event)
++ {
++ bool handled = false;
++
++ // Adjust event position to renderers position
++ QPointF localPos = event->position() - m_plotArea.topLeft();
++ QWheelEvent mappedEvent(localPos,
++ event->globalPosition(),
++ event->pixelDelta(),
++ event->angleDelta(),
++ event->buttons(),
++ event->modifiers(),
++ event->phase(),
++ event->inverted(),
++ event->source());
++ mappedEvent.setAccepted(false);
++
++ if (m_axisRenderer)
++ handled |= m_axisRenderer->handleWheel(&mappedEvent);
++
++ if (!handled)
++ event->ignore();
++ else
++ polishAndUpdate();
++ }
++
++ QSGNode *QGraphsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
++ {
++ Q_UNUSED(updatePaintNodeData);
++
++ for (auto series : std::as_const(m_seriesList)) {
++ qCDebug(lcEvents2D) << "QGraphsView::updatePaintNode." << series;
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer) {
++ if (auto barSeries = qobject_cast<QBarSeries *>(series))
++ m_barsRenderer->updateSeries(barSeries);
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer) {
++ #if QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_area)
++ if (auto lineSeries = qobject_cast<QLineSeries *>(series))
++ m_pointRenderer->updateSeries(lineSeries);
++ #endif
++ #if QT_CONFIG(graphs_2d_scatter)
++ if (auto scatterSeries = qobject_cast<QScatterSeries *>(series))
++ m_pointRenderer->updateSeries(scatterSeries);
++ #endif
++ #if QT_CONFIG(graphs_2d_spline)
++ if (auto splineSeries = qobject_cast<QSplineSeries *>(series))
++ m_pointRenderer->updateSeries(splineSeries);
++ #endif
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer) {
++ if (auto pieSeries = qobject_cast<QPieSeries *>(series))
++ m_pieRenderer->updateSeries(pieSeries);
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer) {
++ if (auto areaSeries = qobject_cast<QAreaSeries *>(series))
++ m_areaRenderer->updateSeries(areaSeries);
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_custom)
++ if (m_customRenderer) {
++ if (auto customSeries = qobject_cast<QCustomSeries *>(series))
++ m_customRenderer->updateSeries(customSeries);
++ }
++ #endif
++ }
++
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer) {
++ auto &cleanupSeriesList = m_cleanupSeriesList[0];
++ m_barsRenderer->afterUpdate(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer) {
++ auto &cleanupSeriesList = m_cleanupSeriesList[1];
++ m_pointRenderer->afterUpdate(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer) {
++ auto &cleanupSeriesList = m_cleanupSeriesList[2];
++ m_areaRenderer->afterUpdate(cleanupSeriesList);
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer) {
++ auto &cleanupSeriesList = m_cleanupSeriesList[3];
++ m_pieRenderer->afterUpdate(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_custom)
++ if (m_customRenderer) {
++ auto &cleanupSeriesList = m_cleanupSeriesList[4];
++ m_customRenderer->afterUpdate(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ }
++ #endif
++
++ // Now possibly dirty theme has been taken into use
++ m_theme->resetThemeDirty();
++
++ return oldNode;
++ }
++
++ void QGraphsView::updatePolish()
++ {
++ qCDebug(lcEvents2D, "QGraphsView::updatePolish. Start Update and polish.");
++
++ Q_TRACE_SCOPE(QGraphs2DGraphsVieUpdatePolish);
++ if (m_axisRenderer) {
++ m_axisRenderer->handlePolish();
++ // Initialize shaders after system's event queue
++ QTimer::singleShot(0, m_axisRenderer, &AxisRenderer::initialize);
++ }
++ if (m_theme && m_theme->isBackgroundVisible()) {
++ if (!m_backgroundRectangle) {
++ // Create m_backgroundRectangle only when it is needed
++ m_backgroundRectangle = new QQuickRectangle(this);
++ m_backgroundRectangle->setZ(-2);
++ }
++ m_backgroundRectangle->setColor(m_theme->backgroundColor());
++ m_backgroundRectangle->setWidth(width());
++ m_backgroundRectangle->setHeight(height());
++ m_backgroundRectangle->setVisible(true);
++ } else if (m_backgroundRectangle) {
++ // Hide and delete the m_backgroundRectangle
++ m_backgroundRectangle->setVisible(false);
++ m_backgroundRectangle->deleteLater();
++ m_backgroundRectangle = nullptr;
++ }
++
++ std::sort(m_seriesList.begin(), m_seriesList.end(), [](QObject *lhs, QObject *rhs) {
++ auto series1 = qobject_cast<QAbstractSeries *>(lhs);
++ auto series2 = qobject_cast<QAbstractSeries *>(rhs);
++
++ if (series1 && series2)
++ return series1->zValue() < series2->zValue();
++ return false;
++ });
++
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer)
++ m_pointRenderer->resetShapePathCount();
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer)
++ m_areaRenderer->resetShapePathCount();
++ #endif
++
++
++ // Polish for all series
++ #if QT_CONFIG(graphs_2d_bar)
++ float highestBarsZ = std::numeric_limits<float>::lowest();
++ int barSeriesIndex = 0;
++ int barSeriesCount =
++ std::count_if(m_seriesList.begin(), m_seriesList.end(),
++ [](const auto &series) { return qobject_cast<QBarSeries *>(series); });
++ #endif
++ for (auto series : std::as_const(m_seriesList)) {
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer) {
++ if (auto barSeries = qobject_cast<QBarSeries *>(series)) {
++ m_barsRenderer->handlePolish(barSeries, barSeriesIndex, barSeriesCount);
++ if (barSeries->zValue() > highestBarsZ)
++ highestBarsZ = barSeries->zValue();
++ barSeriesIndex++;
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ float highestPointZ = std::numeric_limits<float>::lowest();
++
++ if (m_pointRenderer) {
++ #if QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_area)
++ if (auto lineSeries = qobject_cast<QLineSeries *>(series)) {
++ m_pointRenderer->handlePolish(lineSeries);
++ if (lineSeries->zValue() > highestPointZ)
++ highestPointZ = lineSeries->zValue();
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_scatter)
++ if (auto scatterSeries = qobject_cast<QScatterSeries *>(series)) {
++ m_pointRenderer->handlePolish(scatterSeries);
++ if (scatterSeries->zValue() > highestPointZ)
++ highestPointZ = scatterSeries->zValue();
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_spline)
++ if (auto splineSeries = qobject_cast<QSplineSeries *>(series)) {
++ m_pointRenderer->handlePolish(splineSeries);
++ if (splineSeries->zValue() > highestPointZ)
++ highestPointZ = splineSeries->zValue();
++ }
++ #endif
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ float highestPieZ = std::numeric_limits<float>::lowest();
++
++ if (m_pieRenderer) {
++ if (auto pieSeries = qobject_cast<QPieSeries *>(series)) {
++ m_pieRenderer->handlePolish(pieSeries);
++ if (pieSeries->zValue() > highestPieZ)
++ highestPieZ = pieSeries->zValue();
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_area)
++ float highestAreaZ = std::numeric_limits<float>::lowest();
++
++ if (m_areaRenderer) {
++ if (auto areaSeries = qobject_cast<QAreaSeries *>(series)) {
++ m_areaRenderer->handlePolish(areaSeries);
++ if (areaSeries->zValue() > highestAreaZ)
++ highestAreaZ = areaSeries->zValue();
++ }
++ }
++ #endif
++
++ #if QT_CONFIG(graphs_2d_custom)
++ float highestCustomZ = std::numeric_limits<float>::lowest();
++
++ if (m_customRenderer) {
++ if (auto customSeries = qobject_cast<QCustomSeries *>(series)) {
++ m_customRenderer->handlePolish(customSeries);
++ if (customSeries->zValue() > highestCustomZ)
++ highestCustomZ = customSeries->zValue();
++ }
++ }
++ #endif
++ }
++
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer) {
++ auto &cleanupSeriesList = m_cleanupSeriesList[0];
++ m_barsRenderer->afterPolish(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ if (highestBarsZ > std::numeric_limits<float>::lowest())
++ m_barsRenderer->setZ(highestBarsZ);
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer) {
++ float highestPointZ = std::numeric_limits<float>::lowest();
++
++ auto &cleanupSeriesList = m_cleanupSeriesList[1];
++ m_pointRenderer->afterPolish(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ if (highestPointZ > std::numeric_limits<float>::lowest())
++ m_pointRenderer->setZ(highestPointZ);
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer) {
++ float highestAreaZ = std::numeric_limits<float>::lowest();
++
++ auto &cleanupSeriesList = m_cleanupSeriesList[2];
++ m_areaRenderer->afterPolish(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ if (highestAreaZ > std::numeric_limits<float>::lowest())
++ m_areaRenderer->setZ(highestAreaZ);
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer) {
++ float highestPieZ = std::numeric_limits<float>::lowest();
++
++ auto &cleanupSeriesList = m_cleanupSeriesList[3];
++ m_pieRenderer->afterPolish(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ if (highestPieZ > std::numeric_limits<float>::lowest())
++ m_pieRenderer->setZ(highestPieZ);
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_custom)
++ if (m_customRenderer) {
++ float highestCustomZ = std::numeric_limits<float>::lowest();
++
++ auto &cleanupSeriesList = m_cleanupSeriesList[4];
++ m_customRenderer->afterPolish(cleanupSeriesList);
++ cleanupSeriesList.clear();
++ if (highestCustomZ > std::numeric_limits<float>::lowest())
++ m_customRenderer->setZ(highestCustomZ);
++ }
++ #endif
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ if (m_painterItem)
++ m_painterItem->update();
++ #endif
++ }
++
++ void QGraphsView::polishAndUpdate()
++ {
++ polish();
++ update();
++ }
++
++ // ***** Static QQmlListProperty methods *****
++
++ /*!
++ \qmlproperty list GraphsView::seriesList
++
++ List of series that are rendered by the GraphsView. Filled automatically
++ with the series type children of the GraphsView.
++
++ This is the default property, so child elements are automatically added
++ into the series list.
++ \sa BarSeries, LineSeries, ScatterSeries
++ */
++ QQmlListProperty<QObject> QGraphsView::seriesList()
++ {
++ return QQmlListProperty<QObject>(this, this,
++ &QGraphsView::appendSeriesFunc,
++ &QGraphsView::countSeriesFunc,
++ &QGraphsView::atSeriesFunc,
++ &QGraphsView::clearSeriesFunc);
++ }
++
++ void QGraphsView::appendSeriesFunc(QQmlListProperty<QObject> *list, QObject *series)
++ {
++ reinterpret_cast<QGraphsView *>(list->data)->addSeries(series);
++ }
++
++ qsizetype QGraphsView::countSeriesFunc(QQmlListProperty<QObject> *list)
++ {
++ return reinterpret_cast<QGraphsView *>(list->data)->getSeriesList().size();
++ }
++
++ QObject *QGraphsView::atSeriesFunc(QQmlListProperty<QObject> *list, qsizetype index)
++ {
++ return reinterpret_cast<QGraphsView *>(list->data)->getSeriesList().at(index);
++ }
++
++ void QGraphsView::clearSeriesFunc(QQmlListProperty<QObject> *list)
++ {
++ QGraphsView *declItems = reinterpret_cast<QGraphsView *>(list->data);
++ QList<QObject *> realList = declItems->getSeriesList();
++ qsizetype count = realList.size();
++ for (int i = 0; i < count; i++)
++ declItems->removeSeries(realList.at(i));
++ }
++
++ /*!
++ \qmlproperty GraphsTheme GraphsView::theme
++ The theme used by the graph. Determines coloring,
++ axis lines, fonts etc. If theme has not been set,
++ the default theme is used.
++ */
++ QGraphsTheme *QGraphsView::theme() const
++ {
++ return m_theme;
++ }
++
++ void QGraphsView::setTheme(QGraphsTheme *newTheme)
++ {
++ if (m_theme == newTheme) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "theme is already set to:" << newTheme;
++ return;
++ }
++
++ if (m_theme)
++ QObject::disconnect(m_theme, nullptr, this, nullptr);
++
++ m_theme = newTheme;
++
++ if (!m_theme) {
++ m_theme = m_defaultTheme;
++ m_theme->resetColorTheme();
++ }
++
++ QObject::connect(m_theme, &QGraphsTheme::update, this, &QGraphsView::polishAndUpdate);
++ QObject::connect(m_theme, &QGraphsTheme::update,
++ this, &QGraphsView::updateComponentSizes);
++ emit themeChanged();
++ updateComponentSizes();
++ polishAndUpdate();
++ }
++
++ /*!
++ \qmlproperty real GraphsView::marginTop
++ The amount of empty space on the top of the graph.
++ By default, the margin is 20.
++ */
++ qreal QGraphsView::marginTop() const
++ {
++ return m_marginTop;
++ }
++
++ void QGraphsView::setMarginTop(qreal newMarginTop)
++ {
++ if (QtPrivate::fuzzyCompare(m_marginTop, newMarginTop)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginTop);
++ return;
++ }
++ m_marginTop = newMarginTop;
++ updateComponentSizes();
++ polishAndUpdate();
++ emit marginTopChanged();
++ }
++
++ /*!
++ \qmlproperty real GraphsView::marginBottom
++ The amount of empty space on the bottom of the graph.
++ By default, the margin is 20.
++ */
++ qreal QGraphsView::marginBottom() const
++ {
++ return m_marginBottom;
++ }
++
++ void QGraphsView::setMarginBottom(qreal newMarginBottom)
++ {
++ if (QtPrivate::fuzzyCompare(m_marginBottom, newMarginBottom)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginBottom);
++ return;
++ }
++ m_marginBottom = newMarginBottom;
++ updateComponentSizes();
++ polishAndUpdate();
++ emit marginBottomChanged();
++ }
++
++ /*!
++ \qmlproperty real GraphsView::marginLeft
++ The amount of empty space on the left of the graph.
++ By default, the margin is 20.
++ */
++ qreal QGraphsView::marginLeft() const
++ {
++ return m_marginLeft;
++ }
++
++ void QGraphsView::setMarginLeft(qreal newMarginLeft)
++ {
++ if (QtPrivate::fuzzyCompare(m_marginLeft, newMarginLeft)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginLeft);
++ return;
++ }
++ m_marginLeft = newMarginLeft;
++ updateComponentSizes();
++ polishAndUpdate();
++ emit marginLeftChanged();
++ }
++
++ /*!
++ \qmlproperty real GraphsView::marginRight
++ The amount of empty space on the right of the graph.
++ By default, the margin is 20.
++ */
++ qreal QGraphsView::marginRight() const
++ {
++ return m_marginRight;
++ }
++
++ void QGraphsView::setMarginRight(qreal newMarginRight)
++ {
++ if (QtPrivate::fuzzyCompare(m_marginRight, newMarginRight)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginRight);
++ return;
++ }
++ m_marginRight = newMarginRight;
++ updateComponentSizes();
++ polishAndUpdate();
++ emit marginRightChanged();
++ }
++
++ /*!
++ \property QGraphsView::clipPlotArea
++ \since 6.10
++ \brief Controls whether graph items should be clipped
++ if they go outside of a plot area. The default value is \c true.
++
++ \sa QGraphsView::plotArea
++ */
++ /*!
++ \qmlproperty bool GraphsView::clipPlotArea
++ \since 6.10
++ Controls whether graph items should be clipped
++ if they go outside of a plot area. The default value is \c true.
++
++ \sa plotArea
++ */
++ bool QGraphsView::clipPlotArea() const
++ {
++ return m_clipPlotArea;
++ }
++
++ void QGraphsView::setClipPlotArea(bool enabled)
++ {
++ if (m_clipPlotArea == enabled) {
++ qCDebug(lcViewProperties2D, "QGraphsView::setClipPlotArea is already set to %d",
++ enabled);
++ return;
++ }
++
++ m_clipPlotArea = enabled;
++ emit clipPlotAreaChanged();
++ #if QT_CONFIG(graphs_2d_area) || QT_CONFIG(graphs_2d_line) || QT_CONFIG(graphs_2d_scatter) || QT_CONFIG(graphs_2d_spline)
++ if (m_pointRenderer)
++ m_pointRenderer->setClip(m_clipPlotArea);
++ #endif
++ #if QT_CONFIG(graphs_2d_area)
++ if (m_areaRenderer)
++ m_areaRenderer->setClip(m_clipPlotArea);
++ #endif
++ #if QT_CONFIG(graphs_2d_donut_pie)
++ if (m_pieRenderer)
++ m_pieRenderer->setClip(m_clipPlotArea);
++ #endif
++ #if QT_CONFIG(graphs_2d_bar)
++ if (m_barsRenderer)
++ m_barsRenderer->setClip(m_clipPlotArea);
++ #endif
++ #if QT_CONFIG(graphs_2d_custom)
++ if (m_customRenderer)
++ m_customRenderer->setClip(m_clipPlotArea);
++ #endif
++ }
++
++ /*!
++ \property QGraphsView::plotArea
++ \since 6.9
++ \brief The rectangle within which the graph is drawn.
++
++ This is the QGraphsView area minus axis areas and margins.
++ \sa marginTop, marginBottom, marginLeft, marginRight
++ */
++ /*!
++ \qmlproperty rect GraphsView::plotArea
++ \since 6.9
++ The rectangle within which the graph is drawn.
++ This is the GraphsView area minus axis areas and margins.
++ \sa marginTop, marginBottom, marginLeft, marginRight
++ */
++ QRectF QGraphsView::plotArea() const
++ {
++ return m_plotArea;
++ }
++
++ QGraphsView::AxisSizes QGraphsView::axisSizes() const
++ {
++ if (!m_axisRenderer)
++ return {};
++ return {m_axisRenderer->m_y1AxisWidth,
++ m_axisRenderer->m_y2AxisWidth,
++ m_axisRenderer->m_x1AxisHeight,
++ m_axisRenderer->m_x2AxisHeight};
++ }
++
++ void QGraphsView::updateAxisAreas()
++ {
++ const AxisSizes axes = axisSizes();
++
++ QRectF r = { m_marginLeft,
++ m_marginTop,
++ width() - m_marginLeft - m_marginRight,
++ height() - m_marginTop - m_marginBottom };
++
++ const qreal plotWidth = r.width() - axes.y1 - axes.y2;
++ const qreal plotHeight = r.height() - axes.x1 - axes.x2;
++
++ m_x1AxisArea = {r.x() + axes.y1,
++ r.y() + r.height() - axes.x1,
++ plotWidth,
++ axes.x1};
++
++ m_x2AxisArea = {r.x() + axes.y1,
++ r.y(),
++ plotWidth,
++ axes.x2};
++
++ m_y1AxisArea = {r.x(),
++ r.y() + axes.x2,
++ axes.y1,
++ plotHeight};
++
++ m_y2AxisArea = {r.x() + r.width() - axes.y2,
++ r.y() + axes.x2,
++ axes.y2,
++ plotHeight};
++ }
++
++ void QGraphsView::updatePlotArea()
++ {
++ const AxisSizes axes = axisSizes();
++
++ // When axis are in left & bottom
++ qreal x = m_marginLeft;
++ qreal y = m_marginTop;
++ qreal w = width() - x - m_marginRight;
++ qreal h = height() - y - m_marginBottom;
++
++ y += axes.x2;
++ x += axes.y1;
++ h -= axes.x1 + axes.x2;
++ w -= axes.y1 + axes.y2;
++
++ w = qMax(w, 0.0);
++ h = qMax(h, 0.0);
++ QRectF plotArea = QRectF(x, y, w, h);
++ if (plotArea != m_plotArea) {
++ m_plotArea = plotArea;
++ emit plotAreaChanged();
++ }
++ }
++
++ /*!
++ \property QGraphsView::axisX
++ \brief X-axis of this view.
++
++ The x-axis used for the series inside this view.
++
++ \note Setting the same axis to multiple QGraphsViews is not supported.
++ */
++ /*!
++ \qmlproperty AbstractAxis GraphsView::axisX
++ The x-axis used for the series inside this view.
++ \sa axisY
++
++ \note Setting the same axis to multiple GraphsViews is not supported.
++ */
++
++ QAbstractAxis *QGraphsView::axisX() const
++ {
++ return m_axisX;
++ }
++
++ void QGraphsView::setAxisX(QAbstractAxis *axis)
++ {
++ if (m_axisX == axis) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ if (m_axisX)
++ removeAxis(m_axisX);
++ if (axis) {
++ if (axis->alignment() != Qt::AlignBottom && axis->alignment() != Qt::AlignTop)
++ axis->setAlignment(Qt::AlignBottom);
++ if (!addAxis(axis))
++ axis = nullptr;
++ }
++ m_axisX = axis;
++ updateComponentSizes();
++ emit axisXChanged();
++ update();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::axisY
++ \brief Y-axis of this view.
++
++ The y-axis used for the series inside this view.
++
++ \note Setting the same axis to multiple QGraphsViews is not supported.
++ */
++ /*!
++ \qmlproperty AbstractAxis GraphsView::axisY
++ The y-axis used for the series inside this view.
++ \sa axisX
++
++ \note Setting the same axis to multiple GraphsViews is not supported.
++ */
++
++ QAbstractAxis *QGraphsView::axisY() const
++ {
++ return m_axisY;
++ }
++
++ void QGraphsView::setAxisY(QAbstractAxis *axis)
++ {
++ if (m_axisY == axis) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ if (m_axisY)
++ removeAxis(m_axisY);
++ if (axis) {
++ if (axis->alignment() != Qt::AlignLeft && axis->alignment() != Qt::AlignRight)
++ axis->setAlignment(Qt::AlignLeft);
++ if (!addAxis(axis))
++ axis = nullptr;
++ }
++ m_axisY = axis;
++ updateComponentSizes();
++ emit axisYChanged();
++ update();
++ polishAndUpdate();
++ }
++
++ /*!
++ \property QGraphsView::orientation
++ \brief Orientation of the GraphsView.
++
++ Determines the orientation of the QGraphsView. When the orientation is
++ \l {Qt::Horizontal}{Qt::Horizontal}, \l axisX and \l axisY will switch the
++ positions so that \l axisX is rendered vertically and \l axisY horizontally.
++ This property is currently used by the \l QBarSeries.
++ The default value is \l {Qt::Vertical}{Qt::Vertical}.
++ */
++ /*!
++ \qmlproperty Qt.Orientation GraphsView::orientation
++ Determines the orientation of the GraphsView. When the orientation is
++ \l {Qt::Horizontal}{Qt.Horizontal}, \l axisX and \l axisY will switch the
++ positions so that \l axisX is rendered vertically and \l axisY horizontally.
++ This property is currently used by the \l BarSeries.
++ The default value is \l {Qt::Vertical}{Qt.Vertical}.
++ */
++ Qt::Orientation QGraphsView::orientation() const
++ {
++ return m_orientation;
++ }
++
++ void QGraphsView::setOrientation(Qt::Orientation newOrientation)
++ {
++ if (m_orientation == newOrientation) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newOrientation;
++ return;
++ }
++ m_orientation = newOrientation;
++ emit orientationChanged();
++ update();
++ updateComponentSizes();
++ polishAndUpdate();
++ }
++
++ #if QT_CONFIG(graphs_2d_custom)
++ /*
++ mapX and mapY functions are used to map custom series data values
++ to the real pixel values of the GraphsView
++ */
++ qreal QGraphsView::mapX(QCustomSeries *series, qreal x)
++ {
++ return m_customRenderer->mapX(m_axisRenderer, series, x);
++ }
++
++ qreal QGraphsView::mapY(QCustomSeries *series, qreal y)
++ {
++ return m_customRenderer->mapY(m_axisRenderer, series, y);
++ }
++ #endif
++
++ CustomRenderer *QGraphsView::customRenderer() const
++ {
++ return m_customRenderer;
++ }
++
++ /*!
++ \enum QGraphsView::ZoomStyle
++ This enum value describes the zoom style of the graph:
++
++ \value None
++ Zooming is disabled.
++ \value Center
++ Pinch zoom and mouse wheel zoom towards the center of the graph view.
++ */
++
++ /*!
++ \property QGraphsView::zoomStyle
++ \since 6.9
++ \brief Zoom style of the GraphsView.
++
++ Determines the zoom style of the QGraphsView. Zooming works by
++ manipulating the QValueAxis zoom property. The default value
++ is \c {QGraphsView::ZoomStyle::None}.
++ */
++ /*!
++ \qmlproperty enumeration GraphsView::zoomStyle
++ \since 6.9
++ Determines the zoom style of the GraphsView. Zooming works by
++ manipulating the ValueAxis zoom property. The default value
++ is \c {GraphsView.ZoomStyle.None}.
++
++ \value GraphsView.ZoomStyle.None
++ Zooming is disabled.
++ \value GraphsView.ZoomStyle.Center
++ Pinch zoom and mouse wheel zoom towards the center of the graph view.
++
++ */
++ QGraphsView::ZoomStyle QGraphsView::zoomStyle() const
++ {
++ return m_zoomStyle;
++ }
++
++ void QGraphsView::setZoomStyle(ZoomStyle newZoomStyle)
++ {
++ if (m_zoomStyle == newZoomStyle) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newZoomStyle;
++ return;
++ }
++ m_zoomStyle = newZoomStyle;
++ emit zoomStyleChanged();
++ }
++
++ /*!
++ \enum QGraphsView::PanStyle
++ This enum value describes the pan style of the graph:
++
++ \value None
++ Panning is disabled.
++ \value Drag
++ Mouse and touch drag pan the view around.
++ */
++
++ /*!
++ \property QGraphsView::panStyle
++ \since 6.9
++ \brief Pan style of the GraphsView.
++
++ Determines the pan style of the QGraphsView. Panning works by
++ manipulating the pan property of a QValueAxis.
++ The default value is \c {QGraphsView::PanStyle::None}.
++ */
++ /*!
++ \qmlproperty enumeration GraphsView::panStyle
++ \since 6.9
++ Determines the pan style of the GraphsView. Panning works by
++ manipulating the pan property of a ValueAxis.
++ The default value is \c {GraphsView.PanStyle.None}.
++
++ \value GraphsView.PanStyle.None
++ Panning is disabled.
++ \value GraphsView.PanStyle.Drag
++ Mouse and touch drag pan the view around.
++ */
++ QGraphsView::PanStyle QGraphsView::panStyle() const
++ {
++ return m_panStyle;
++ }
++
++ void QGraphsView::setPanStyle(PanStyle newPanStyle)
++ {
++ if (m_panStyle == newPanStyle) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newPanStyle;
++ return;
++ }
++ m_panStyle = newPanStyle;
++ emit panStyleChanged();
++ }
++
++ /*!
++ \property QGraphsView::zoomAreaEnabled
++ \since 6.9
++ \brief Enables zoom area
++
++ Zoom area changes mouse and touch dragging to draw a box determined
++ by \c zoomAreaDelegate. Upon release the graph QValueAxis zoom and pan
++ properties are changed so that the view covers only the area intersected
++ by the drawn box.
++ \sa zoomAreaDelegate
++ */
++ /*!
++ \qmlproperty bool GraphsView::zoomAreaEnabled
++ \since 6.9
++ Zoom area changes mouse and touch dragging to draw a box determined
++ by \c zoomAreaDelegate. Upon release the graph ValueAxis zoom and pan
++ properties are changed so that the view covers only the area intersected
++ by the drawn box.
++ \sa zoomAreaDelegate
++ */
++ bool QGraphsView::zoomAreaEnabled() const
++ {
++ return m_zoomAreaEnabled;
++ }
++
++ void QGraphsView::setZoomAreaEnabled(bool newZoomAreaEnabled)
++ {
++ if (m_zoomAreaEnabled == newZoomAreaEnabled) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newZoomAreaEnabled;
++ return;
++ }
++ m_zoomAreaEnabled = newZoomAreaEnabled;
++ emit zoomAreaEnabledChanged();
++ }
++
++ /*!
++ \property QGraphsView::zoomAreaDelegate
++ \since 6.9
++ \brief Zoom area visual delegate
++
++ Determines the QML element that is drawn when the user performs a drag
++ motion to zoom in to an area.
++ */
++ /*!
++ \qmlproperty Component GraphsView::zoomAreaDelegate
++ \since 6.9
++ Determines the QML element that is drawn when the user performs a drag
++ motion to zoom in to an area.
++ */
++ QQmlComponent *QGraphsView::zoomAreaDelegate() const
++ {
++ return m_zoomAreaDelegate;
++ }
++
++ void QGraphsView::setZoomAreaDelegate(QQmlComponent *newZoomAreaDelegate)
++ {
++ if (m_zoomAreaDelegate == newZoomAreaDelegate) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newZoomAreaDelegate;
++ return;
++ }
++ m_zoomAreaDelegate = newZoomAreaDelegate;
++
++ if (m_zoomAreaDelegate) {
++ m_zoomAreaItem = qobject_cast<QQuickItem *>(
++ m_zoomAreaDelegate->create(m_zoomAreaDelegate->creationContext()));
++ m_zoomAreaItem->setParent(this);
++ m_zoomAreaItem->setParentItem(this);
++ m_zoomAreaItem->setVisible(false);
++ }
++
++ emit zoomAreaDelegateChanged();
++ }
++
++ /*!
++ \property QGraphsView::zoomSensitivity
++ \since 6.9
++ \brief Zoom value change sensitivity
++
++ Determines how fast zoom value changes while zooming.
++ */
++ /*!
++ \qmlproperty real GraphsView::zoomSensitivity
++ \since 6.9
++ Determines how fast zoom value changes while zooming.
++ */
++ qreal QGraphsView::zoomSensitivity() const
++ {
++ return m_zoomSensitivity;
++ }
++
++ void QGraphsView::setZoomSensitivity(qreal newZoomSensitivity)
++ {
++ if (QtPrivate::fuzzyCompare(m_zoomSensitivity, newZoomSensitivity)) {
++ qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), newZoomSensitivity);
++ return;
++ }
++ m_zoomSensitivity = newZoomSensitivity;
++ emit zoomSensitivityChanged();
++ }
++
++ /*!
++ \property QGraphsView::dynamicLabelMargins
++ \brief Calculate label margins dynamically based on content
++
++ This property holds whether label margins adjust dynamically.
++
++ By default, labels wider than the allocated margin overlap other
++ graph elements. When enabled, the renderer reserves extra space
++ for such labels and repositions axes to prevent overlap.
++ */
++ /*!
++ \qmlproperty bool GraphsView::dynamicLabelMargins
++ This property holds whether label margins adjust dynamically.
++
++ By default, labels wider than the allocated margin overlap other
++ graph elements. When enabled, the renderer reserves extra space
++ for such labels and repositions axes to prevent overlap.
++ */
++ bool QGraphsView::dynamicLabelMargins() const
++ {
++ return m_dynamicLabelMargins;
++ }
++
++ void QGraphsView::setDynamicLabelMargins(bool newDynamicLabelMargins)
++ {
++ if (m_dynamicLabelMargins == newDynamicLabelMargins) {
++ qCDebug(lcViewProperties2D) << __FUNCTION__
++ << "value is already set to:" << newDynamicLabelMargins;
++ return;
++ }
++ m_dynamicLabelMargins = newDynamicLabelMargins;
++ updateComponentSizes();
++ emit dynamicLabelMarginsChanged();
++ polishAndUpdate();
++ }
++
++ int QGraphsView::getSeriesRendererIndex(QAbstractSeries *series)
++ {
++ int index = 0;
++ if (series) {
++ switch (QAbstractSeriesPrivate::get(series)->type()) {
++ case QAbstractSeries::SeriesType::Bar:
++ index = 0;
++ break;
++ case QAbstractSeries::SeriesType::Scatter:
++ case QAbstractSeries::SeriesType::Line:
++ case QAbstractSeries::SeriesType::Spline:
++ index = 1;
++ break;
++ case QAbstractSeries::SeriesType::Area:
++ index = 2;
++ break;
++ case QAbstractSeries::SeriesType::Pie:
++ index = 3;
++ break;
++ case QAbstractSeries::SeriesType::Custom:
++ index = 4;
++ break;
++ }
++ }
++ return index;
++ }
++
++ /*!
++ \qmlproperty bool GraphsView::useCanvasPainter
++ \since 6.12
++ This property holds whether the graph draws with \l {Qt Canvas Painter}
++ instead of \l {Qt Quick Shapes}. The default is \c false, except in a
++ build where the high-performance backend is the only one available.
++
++ In a build without the high-performance backend, this property stays
++ \c false and setting it has no effect.
++
++ For a comparison of the two renderers and for how to build them, see
++ \l {Qt Graphs 2D Rendering Backends}.
++ */
++ bool QGraphsView::useCanvasPainter() const
++ {
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ return m_useCanvasPainter;
++ #else
++ return false;
++ #endif
++ }
++
++ void QGraphsView::setUseCanvasPainter(bool newUseCanvasPainter)
++ {
++ #if QT_CONFIG(graphs_2d_high_performance_backend)
++ if (m_useCanvasPainter == newUseCanvasPainter)
++ return;
++ m_useCanvasPainter = newUseCanvasPainter;
++
++ if (m_useCanvasPainter)
++ createCanvasPainter();
++ else
++ removeCanvasPainter();
++
++ emit useCanvasPainterChanged();
++ // The renderers only refresh their backend-specific geometry during a polish, so without
++ // this the outgoing backend's last-drawn output would linger until something else happened
++ // to trigger one.
++ update();
++ polishAndUpdate();
++ #else
++ Q_UNUSED(newUseCanvasPainter);
++ #endif
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qgraphsview_p.cpp"