1 | // Copyright (C) 2024 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtCore/QList> |
5 | #include "QtGraphs/QSplineSeries" |
6 | #include "QtGraphs/QXYSeries" |
7 | #include "private/qgraphtransition_p.h" |
8 | #include "private/qsplinecontrolanimation_p.h" |
9 | #include "private/qsplineseries_p.h" |
10 | #include "private/qxyseries_p.h" |
11 | #include "private/qxyseriesanimation_p.h" |
12 | |
13 | /*! |
14 | \qmltype GraphTransition |
15 | \inqmlmodule QtGraphs |
16 | \ingroup graphs_qml_2D |
17 | \brief A container in which all animations are defined. |
18 | |
19 | GraphTransition is a container for animations inside on Graphs2D. Define this class |
20 | inside a graph type to enable animated changes for XYSeries within 2D graphs. To |
21 | define individual animations, add them inside of the GraphTransition. The |
22 | individual animations within the same GraphTransition are animated in parallel. If |
23 | a GraphTransition is found by the graph during a call to a supported function |
24 | which appends or replaces a point, then the values are interpolated according to |
25 | the animations that are added. |
26 | |
27 | This example shows how to define a GraphTransition within a graph. |
28 | |
29 | \snippet doc_src_qmlgraphs.cpp 11 |
30 | |
31 | \note GraphTransition requires it to be defined directly inside the graph which needs to be animated. |
32 | Currently only XYSeries are supported. |
33 | |
34 | \sa GraphPointAnimation, SplineControlAnimation |
35 | */ |
36 | |
37 | /*! |
38 | \qmlproperty enumeration GraphTransition::TransitionType |
39 | |
40 | Type of the transition. |
41 | |
42 | \value None |
43 | No transition. |
44 | \value PointAdded |
45 | A point has been added. |
46 | \value PointReplaced |
47 | A point has been replaced. |
48 | \value PointRemoved |
49 | A point has been removed. |
50 | */ |
51 | |
52 | QGraphTransition::QGraphTransition(QObject *parent) |
53 | : QObject(parent) |
54 | , m_animationGroup(this) |
55 | , m_initialized(false) |
56 | {} |
57 | |
58 | QGraphTransition::~QGraphTransition() {} |
59 | |
60 | void QGraphTransition::componentComplete() |
61 | { |
62 | // Currently only assuming animations in QXYSeries |
63 | Q_ASSERT(parent() != nullptr && qobject_cast<QXYSeries *>(parent())); |
64 | auto series = qobject_cast<QXYSeries *>(object: parent()); |
65 | |
66 | if (series) |
67 | series->d_func()->m_graphTransition = this; |
68 | } |
69 | |
70 | void QGraphTransition::onPointChanged(TransitionType type, int index, QPointF point) |
71 | { |
72 | auto series = qobject_cast<QXYSeries *>(object: parent()); |
73 | |
74 | if (!series || !series->hasLoaded()) |
75 | return; |
76 | |
77 | if (m_animationGroup.state() == QAbstractAnimation::Running) |
78 | m_animationGroup.stop(); |
79 | |
80 | for (auto child : m_animationGroup.children()) { |
81 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: child); |
82 | childAnimation->updateCurrent(tt: type, index, point); |
83 | } |
84 | |
85 | for (auto child : m_animationGroup.children()) { |
86 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: child); |
87 | |
88 | childAnimation->animate(); |
89 | } |
90 | |
91 | auto spline = qobject_cast<QSplineSeries *>(object: series); |
92 | |
93 | if (spline && !contains(type: QGraphAnimation::GraphAnimationType::ControlPoint)) |
94 | spline->d_func()->calculateSplinePoints(); |
95 | |
96 | m_animationGroup.start(); |
97 | } |
98 | |
99 | void QGraphTransition::initialize() |
100 | { |
101 | auto series = qobject_cast<QXYSeries *>(object: parent()); |
102 | |
103 | if (!series || m_initialized) |
104 | return; |
105 | |
106 | const auto &animationChildren = m_animationGroup.children(); |
107 | for (qsizetype i = 0; i < animationChildren.size(); ++i) { |
108 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: animationChildren[i]); |
109 | |
110 | //GraphPointAnimation needs to be the first for the transition to work |
111 | if (childAnimation->animationType() == QGraphAnimation::GraphAnimationType::GraphPoint |
112 | && i != 0) |
113 | return; |
114 | } |
115 | |
116 | m_initialized = true; |
117 | } |
118 | |
119 | void QGraphTransition::stop() |
120 | { |
121 | m_animationGroup.stop(); |
122 | |
123 | for (auto child : m_animationGroup.children()) { |
124 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: child); |
125 | childAnimation->end(); |
126 | } |
127 | } |
128 | |
129 | bool QGraphTransition::initialized() |
130 | { |
131 | return m_initialized; |
132 | } |
133 | |
134 | bool QGraphTransition::contains(QGraphAnimation::GraphAnimationType type) |
135 | { |
136 | for (const auto anim : m_animations) { |
137 | if (anim->animationType() == type) |
138 | return true; |
139 | } |
140 | |
141 | return false; |
142 | } |
143 | |
144 | /*! |
145 | \qmlproperty list<object> GraphTransition::animations |
146 | A container for all the animations in the GraphTransition. |
147 | Currently only supports animations to be added and cleared. |
148 | By default, the list is empty. |
149 | */ |
150 | QQmlListProperty<QObject> QGraphTransition::animations() |
151 | { |
152 | return QQmlListProperty<QObject>{this, |
153 | nullptr, |
154 | &QGraphTransition::append, |
155 | nullptr, |
156 | nullptr, |
157 | &QGraphTransition::clear}; |
158 | } |
159 | |
160 | void QGraphTransition::classBegin() {} |
161 | |
162 | void QGraphTransition::append(QQmlListProperty<QObject> *animationProps, QObject *animation) |
163 | { |
164 | auto graphTransition = qobject_cast<QGraphTransition *>(object: animationProps->object); |
165 | |
166 | if (graphTransition) { |
167 | auto graphAnimation = qobject_cast<QGraphAnimation *>(object: animation); |
168 | graphTransition->m_animations.append(t: graphAnimation); |
169 | graphTransition->m_animationGroup.addAnimation(animation: graphAnimation); |
170 | } |
171 | } |
172 | |
173 | void QGraphTransition::clear(QQmlListProperty<QObject> *) |
174 | { |
175 | Q_UNIMPLEMENTED(); |
176 | } |
177 | |