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 | QGraphTransition::QGraphTransition(QObject *parent) |
38 | : QObject(parent) |
39 | , m_animationGroup(this) |
40 | , m_initialized(false) |
41 | {} |
42 | |
43 | QGraphTransition::~QGraphTransition() {} |
44 | |
45 | void QGraphTransition::componentComplete() |
46 | { |
47 | // Currently only assuming animations in QXYSeries |
48 | Q_ASSERT(parent() != nullptr && qobject_cast<QXYSeries *>(parent())); |
49 | auto series = qobject_cast<QXYSeries *>(object: parent()); |
50 | |
51 | if (series) |
52 | series->d_func()->m_graphTransition = this; |
53 | } |
54 | |
55 | void QGraphTransition::onPointChanged(TransitionType type, int index, QPointF point) |
56 | { |
57 | auto series = qobject_cast<QXYSeries *>(object: parent()); |
58 | |
59 | if (!series || !series->hasLoaded()) |
60 | return; |
61 | |
62 | if (m_animationGroup.state() == QAbstractAnimation::Running) |
63 | m_animationGroup.stop(); |
64 | |
65 | for (auto child : m_animationGroup.children()) { |
66 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: child); |
67 | childAnimation->updateCurrent(tt: type, index, point); |
68 | } |
69 | |
70 | for (auto child : m_animationGroup.children()) { |
71 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: child); |
72 | |
73 | childAnimation->animate(); |
74 | } |
75 | |
76 | auto spline = qobject_cast<QSplineSeries *>(object: series); |
77 | |
78 | if (spline && !contains(type: QGraphAnimation::GraphAnimationType::ControlPoint)) |
79 | spline->d_func()->calculateSplinePoints(); |
80 | |
81 | m_animationGroup.start(); |
82 | } |
83 | |
84 | void QGraphTransition::initialize() |
85 | { |
86 | auto series = qobject_cast<QXYSeries *>(object: parent()); |
87 | |
88 | if (!series || m_initialized) |
89 | return; |
90 | |
91 | const auto &animationChildren = m_animationGroup.children(); |
92 | for (qsizetype i = 0; i < animationChildren.size(); ++i) { |
93 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: animationChildren[i]); |
94 | |
95 | //GraphPointAnimation needs to be the first for the transition to work |
96 | if (childAnimation->animationType() == QGraphAnimation::GraphAnimationType::GraphPoint |
97 | && i != 0) |
98 | return; |
99 | } |
100 | |
101 | m_initialized = true; |
102 | } |
103 | |
104 | void QGraphTransition::stop() |
105 | { |
106 | m_animationGroup.stop(); |
107 | |
108 | for (auto child : m_animationGroup.children()) { |
109 | auto childAnimation = qobject_cast<QXYSeriesAnimation *>(object: child); |
110 | childAnimation->end(); |
111 | } |
112 | } |
113 | |
114 | bool QGraphTransition::initialized() |
115 | { |
116 | return m_initialized; |
117 | } |
118 | |
119 | bool QGraphTransition::contains(QGraphAnimation::GraphAnimationType type) |
120 | { |
121 | for (const auto anim : m_animations) { |
122 | if (anim->animationType() == type) |
123 | return true; |
124 | } |
125 | |
126 | return false; |
127 | } |
128 | |
129 | /*! |
130 | \qmlproperty list<object> GraphTransition::animations |
131 | A container for all the animations in the GraphTransition. |
132 | Currently only supports animations to be added and cleared. |
133 | By default, the list is empty. |
134 | */ |
135 | QQmlListProperty<QObject> QGraphTransition::animations() |
136 | { |
137 | return QQmlListProperty<QObject>{this, |
138 | nullptr, |
139 | &QGraphTransition::append, |
140 | nullptr, |
141 | nullptr, |
142 | &QGraphTransition::clear}; |
143 | } |
144 | |
145 | void QGraphTransition::classBegin() {} |
146 | |
147 | void QGraphTransition::append(QQmlListProperty<QObject> *animationProps, QObject *animation) |
148 | { |
149 | auto graphTransition = qobject_cast<QGraphTransition *>(object: animationProps->object); |
150 | |
151 | if (graphTransition) { |
152 | auto graphAnimation = qobject_cast<QGraphAnimation *>(object: animation); |
153 | graphTransition->m_animations.append(t: graphAnimation); |
154 | graphTransition->m_animationGroup.addAnimation(animation: graphAnimation); |
155 | } |
156 | } |
157 | |
158 | void QGraphTransition::clear(QQmlListProperty<QObject> *) |
159 | { |
160 | Q_UNIMPLEMENTED(); |
161 | } |
162 | |