Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
PhosphorProfile.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 fuddlesworth
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
9#include <PhosphorAnimation/phosphoranimation_export.h>
10
11#include <QtCore/QJsonObject>
12#include <QtCore/QObject>
13#include <QtCore/QString>
14#include <QtCore/QtMath>
15#include <QtQml/qqmlregistration.h>
16
17#include <cmath>
18
19namespace PhosphorAnimation {
20
69class PHOSPHORANIMATION_EXPORT PhosphorProfile
70{
71 Q_GADGET
72 QML_VALUE_TYPE(phosphorProfile)
73 QML_STRUCTURED_VALUE
74
75 Q_PROPERTY(PhosphorCurve curve READ curve WRITE setCurve)
76 Q_PROPERTY(qreal duration READ duration WRITE setDuration)
77 Q_PROPERTY(int minDistance READ minDistance WRITE setMinDistance)
78 Q_PROPERTY(SequenceMode sequenceMode READ sequenceMode WRITE setSequenceMode)
79 Q_PROPERTY(int staggerInterval READ staggerInterval WRITE setStaggerInterval)
80 Q_PROPERTY(QString presetName READ presetName WRITE setPresetName)
81
82public:
83 // Parallel to `PhosphorAnimation::SequenceMode` so QML enum
84 // references work without touching the C++ scope. Integer values
85 // match for `static_cast` round-trip — matches decision O's "do
86 // not rename enum values" convention used by `PhosphorEasing::Type`.
91 Q_ENUM(SequenceMode)
92
93 PhosphorProfile() = default;
95 explicit PhosphorProfile(const Profile& value)
96 : m_value(value)
97 {
98 }
99
106 const Profile& value() const
107 {
108 return m_value;
109 }
110
111 // ─── Property delegates ───
112 //
113 // Setters apply the same validation as `Profile::fromJson`: NaN/inf and
114 // out-of-range values are rejected, so `effective*()` substitutes the
115 // library default and a QML script that passes garbage gets the same
116 // fault-tolerant behaviour as a malformed profile JSON file rather than
117 // landing pathological values into a QQuickPropertyAnimation downstream.
118 //
119 // Two deliberate differences from `fromJson`. A rejection here leaves the
120 // field UNSET rather than substituting an engaged default, including for
121 // `sequenceMode` — this value type is a plugin-facing handle, not a node in
122 // the ProfileTree, so nothing inherits through it and there is no
123 // inheritance to block. And a rejection here is silent: the JSON path warns
124 // with the offending value because it is parsing a file a user hand-edited,
125 // whereas this is a programming error in the calling script and the value
126 // is visible in the script itself.
127 //
128 // `setCurve` is outside all of the above: a `PhosphorCurve` is either a
129 // valid curve handle or null, so there is nothing to range-check. Assigning
130 // a null one DISENGAGES the field rather than rejecting anything.
131
133 {
134 return PhosphorCurve(m_value.curve);
135 }
136 void setCurve(const PhosphorCurve& c)
137 {
138 m_value.curve = c.curve();
139 }
140
141 qreal duration() const
142 {
143 return m_value.effectiveDuration();
144 }
145 void setDuration(qreal ms)
146 {
147 if (!std::isfinite(ms) || ms <= 0.0 || ms > Profile::MaxDurationMs) {
148 m_value.duration.reset();
149 return;
150 }
151 m_value.duration = ms;
152 }
153
154 int minDistance() const
155 {
156 return m_value.effectiveMinDistance();
157 }
158 void setMinDistance(int px)
159 {
160 if (px < 0 || px > Profile::MaxMinDistancePx) {
161 m_value.minDistance.reset();
162 return;
163 }
164 m_value.minDistance = px;
165 }
166
168 {
169 return static_cast<SequenceMode>(static_cast<int>(m_value.effectiveSequenceMode()));
170 }
172 {
173 // Validated like its scalar siblings, and like them a rejection leaves
174 // the field unset rather than substituting an engaged default the way
175 // `Profile::fromJson` does (see the block comment above for why the two
176 // differ). A QML script can assign any int to a Q_ENUM property, and an
177 // unknown enumerator stored here would be returned verbatim by
178 // `effectiveSequenceMode()` and serialized by `toJson()`, so the wrapper
179 // would emit a blob its own `fromJson` rejects on the next read.
180 const int raw = static_cast<int>(mode);
181 if (raw != static_cast<int>(PhosphorAnimation::SequenceMode::AllAtOnce)
182 && raw != static_cast<int>(PhosphorAnimation::SequenceMode::Cascade)) {
183 m_value.sequenceMode.reset();
184 return;
185 }
186 m_value.sequenceMode = static_cast<PhosphorAnimation::SequenceMode>(raw);
187 }
188
189 int staggerInterval() const
190 {
191 return m_value.effectiveStaggerInterval();
192 }
194 {
195 if (ms < 0 || ms > Profile::MaxStaggerIntervalMs) {
196 m_value.staggerInterval.reset();
197 return;
198 }
199 m_value.staggerInterval = ms;
200 }
201
202 QString presetName() const
203 {
204 return m_value.presetName.value_or(QString());
205 }
206 void setPresetName(const QString& name)
207 {
208 m_value.presetName = name;
209 }
210
211 // ─── Serialization ───
212
216 Q_INVOKABLE QJsonObject toJson() const
217 {
218 return m_value.toJson();
219 }
220
234 Q_INVOKABLE static PhosphorProfile fromJson(const QJsonObject& obj)
235 {
236 static CurveRegistry sFallback;
237 CurveRegistry* registry = PhosphorCurve::defaultRegistry();
238 return PhosphorProfile(Profile::fromJson(obj, registry ? *registry : sFallback));
239 }
240
241 // ─── Equality ───
242
243 bool operator==(const PhosphorProfile& other) const
244 {
245 return m_value == other.m_value;
246 }
247 bool operator!=(const PhosphorProfile& other) const
248 {
249 return !(*this == other);
250 }
251
252private:
253 Profile m_value;
254};
255
256} // namespace PhosphorAnimation
257
258Q_DECLARE_METATYPE(PhosphorAnimation::PhosphorProfile)
String-id <-> curve factory registry.
Definition CurveRegistry.h:26
Opaque QML value-type wrapper around shared_ptr<const Curve>.
Definition PhosphorCurve.h:47
std::shared_ptr< const Curve > curve() const
The wrapped pointer. May be null on a default-constructed handle.
Definition PhosphorCurve.h:64
QML value-type wrapper around PhosphorAnimation::Profile.
Definition PhosphorProfile.h:70
void setCurve(const PhosphorCurve &c)
Definition PhosphorProfile.h:136
QString presetName() const
Definition PhosphorProfile.h:202
SequenceMode sequenceMode() const
Definition PhosphorProfile.h:167
QJsonObject toJson() const
Serialize to a JSON object via Profile::toJson.
Definition PhosphorProfile.h:216
void setPresetName(const QString &name)
Definition PhosphorProfile.h:206
void setDuration(qreal ms)
Definition PhosphorProfile.h:145
PhosphorCurve curve() const
Definition PhosphorProfile.h:132
SequenceMode
Definition PhosphorProfile.h:87
bool operator!=(const PhosphorProfile &other) const
Definition PhosphorProfile.h:247
void setMinDistance(int px)
Definition PhosphorProfile.h:158
static PhosphorProfile fromJson(const QJsonObject &obj)
Parse from a JSON object.
Definition PhosphorProfile.h:234
int minDistance() const
Definition PhosphorProfile.h:154
qreal duration() const
Definition PhosphorProfile.h:141
int staggerInterval() const
Definition PhosphorProfile.h:189
bool operator==(const PhosphorProfile &other) const
Definition PhosphorProfile.h:243
void setSequenceMode(SequenceMode mode)
Definition PhosphorProfile.h:171
void setStaggerInterval(int ms)
Definition PhosphorProfile.h:193
const Profile & value() const
Read-only access to the underlying value.
Definition PhosphorProfile.h:106
Configuration for a single animation event.
Definition Profile.h:33
Definition AnimatedValue.h:32
SequenceMode
How a batch of animations starts. Numeric values match the historical wire format.
Definition Profile.h:21