6#include <QtGui/private/qguisvg_p.h>
10using namespace Qt::Literals::StringLiterals;
11using namespace QSvgCssValues;
15int parseCssClockValue(QStringView str,
bool *ok)
20 if (str.endsWith(
"ms"_L1)) {
23 }
else if (str.endsWith(
"s"_L1)) {
30 double val = ms * str.toDouble(ok);
32 if (val > std::numeric_limits<
int>::min() && val < std::numeric_limits<
int>::max())
33 res =
static_cast<
int>(val);
40bool isTimeValue(QStringView str) {
41 if (str.endsWith(
"ms"_L1))
43 else if (str.endsWith(
"s"_L1))
49 Q_UNUSED(str.toDouble(&ok))
53bool isIteration(QStringView str) {
54 if (str ==
"infinite"_L1)
57 Q_UNUSED(str.toDouble(&ok))
61bool isDirection(QStringView str) {
62 return str ==
"normal"_L1 || str ==
"reverse"_L1 || str.startsWith(
"alternate"_L1);
65bool isTimingFunction(QStringView str) {
66 return str.startsWith(
"linear"_L1) || str.startsWith(
"ease"_L1)
67 || str.startsWith(
"step"_L1) || str.startsWith(
"cubic-bezier"_L1);
70bool isFillMode(QStringView str) {
71 return str ==
"none"_L1 || str ==
"forwards"_L1 || str ==
"backwards"_L1 || str ==
"both"_L1;
74bool isPlayState(QStringView str) {
75 return str ==
"paused"_L1 || str ==
"running"_L1;
82 QRegularExpression re(
";| "_L1);
83 for (
int i = 0; i < attributes.size(); ++i) {
84 const QXmlStreamAttribute &attribute = attributes.at(i);
85 QStringView name = attribute.qualifiedName();
88 QStringView value = attribute.value();
89 switch (name.at(0).unicode()) {
92 if (name ==
"animation"_L1)
93 shortHandtoLonghandForm(value);
94 if (name ==
"animation-name"_L1)
95 m_names = value.split(re, Qt::SkipEmptyParts);
96 if (name ==
"animation-duration"_L1)
97 m_durations = value.split(re, Qt::SkipEmptyParts);
98 if (name ==
"animation-delay"_L1)
99 m_delays = value.split(re, Qt::SkipEmptyParts);
100 if (name ==
"animation-iteration-count"_L1)
101 m_iterationCounts = value.split(re, Qt::SkipEmptyParts);
102 if (name ==
"animation-direction"_L1)
103 m_directions = value.split(re, Qt::SkipEmptyParts);
104 if (name ==
"animation-timing-function"_L1)
105 m_timingFunctions = value.split(re, Qt::SkipEmptyParts);
106 if (name ==
"animation-fill-mode"_L1)
107 m_fillModes = value.split(re, Qt::SkipEmptyParts);
108 if (name ==
"animation-play-state"_L1)
109 m_playStates = value.split(re, Qt::SkipEmptyParts);
113 if (name ==
"offset-path"_L1)
114 m_offsetPath = value;
115 if (name ==
"offset-distance"_L1)
116 m_offsetDistance = value;
117 if (name ==
"offset-rotate"_L1)
118 m_offsetRotate = value;
130 QList<QSvgAnimationProperty> parsedProperties;
131 for (
int i = 0; i < m_names.size(); i++) {
132 QSvgAnimationProperty property;
133 property.name = m_names.at(i);
135 if (!m_durations.isEmpty()) {
136 QStringView durationStr = m_durations.at(i % m_durations.size());
137 int duration = parseCssClockValue(durationStr, &ok);
138 property.duration = ok ? duration : 0;
141 if (!m_delays.isEmpty()) {
142 QStringView delayStr = m_delays.at(i % m_delays.size());
143 int delay = parseCssClockValue(delayStr, &ok);
144 property.delay = ok ? delay : 0;
147 if (!m_iterationCounts.isEmpty()) {
148 QStringView iterationStr = m_iterationCounts.at(i % m_iterationCounts.size());
150 if (iterationStr ==
"infinite"_L1) {
153 qreal count = iterationStr.toDouble(&ok);
154 iteration = ok ? qMax(1.0, count) : 0;
156 property.iteration = iteration;
159 if (!m_timingFunctions.isEmpty()) {
160 QStringView timingFunctionStr = m_timingFunctions.at(i % m_timingFunctions.size());
161 if (timingFunctionStr ==
"linear"_L1) {
162 property.easingFunction = EasingFunction::Linear;
163 }
else if (timingFunctionStr ==
"ease-in"_L1) {
164 property.easingFunction = EasingFunction::EaseIn;
165 }
else if (timingFunctionStr ==
"ease-out"_L1) {
166 property.easingFunction = EasingFunction::EaseOut;
167 }
else if (timingFunctionStr ==
"ease-in-out"_L1) {
168 property.easingFunction = EasingFunction::EaseInOut;
169 }
else if (timingFunctionStr ==
"step-end"_L1) {
170 property.easingFunction = EasingFunction::Steps;
171 property.easingValues = StepValues{quint32(1), QSvgCssValues::StepPosition::End};
172 }
else if (timingFunctionStr ==
"step-start"_L1) {
173 property.easingFunction = EasingFunction::Steps;
174 property.easingValues = StepValues{quint32(1), QSvgCssValues::StepPosition::Start};
177 parsedProperties.append(property);
180 return parsedProperties;
187 offset.path = QGuiSvg::parsePath(m_offsetPath);
190 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
191 if ((index = m_offsetRotate.indexOf(
"auto"_L1, 0, cs)) >= 0) {
192 std::optional<qreal> angle = QGuiSvg::parseAngle(m_offsetRotate.sliced(index + 4));
193 offset.rotateType = angle ? QtSvg::OffsetRotateType::AutoAngle :
194 QtSvg::OffsetRotateType::Auto;
195 offset.angle = angle.value_or(0);
196 }
else if ((index = m_offsetRotate.indexOf(
"reverse"_L1, 0, cs)) >= 0) {
197 std::optional<qreal> angle = QGuiSvg::parseAngle(m_offsetRotate.sliced(index + 7));
198 offset.rotateType = angle ? QtSvg::OffsetRotateType::ReverseAngle :
199 QtSvg::OffsetRotateType::Reverse;
200 offset.angle = angle.value_or(0);
202 std::optional<qreal> angle = QGuiSvg::parseAngle(m_offsetRotate);
203 offset.rotateType = angle ? QtSvg::OffsetRotateType::Angle :
204 QtSvg::OffsetRotateType::Auto;
205 offset.angle = angle.value_or(0);
208 QGuiSvg::LengthType type;
210 offset.distance= QGuiSvg::parseLength(m_offsetDistance, &type, ok);
211 if (type == QGuiSvg::LengthType::LT_PERCENT) {
212 offset.distance = offset.distance / 100.0;
223 m_iterationCounts.clear();
224 m_directions.clear();
225 m_timingFunctions.clear();
227 m_playStates.clear();
229 enum Property : uchar {
232 IterationCount = 1 << 2,
234 TimingFunction = 1 << 4,
240 const QList<QStringView> animations = value.split(QLatin1Char(
';'), Qt::SkipEmptyParts);
241 for (
const QStringView &animation : animations) {
242 uchar propertyFlag = 0;
243 const QList<QStringView> animationProperties = animation.split(QLatin1Char(
' '), Qt::SkipEmptyParts);
244 for (
const QStringView &property : animationProperties) {
245 if (!(propertyFlag & Property::Duration) && isTimeValue(property)) {
246 m_durations.append(property);
247 propertyFlag |= Property::Duration;
248 }
else if (!(propertyFlag & Property::Delay) && isTimeValue(property)) {
249 m_delays.append(property);
250 propertyFlag |= Property::Delay;
251 }
else if (!(propertyFlag & Property::IterationCount) && isIteration(property)) {
252 m_iterationCounts.append(property);
253 propertyFlag |= Property::IterationCount;
254 }
else if (!(propertyFlag & Property::Direction) && isDirection(property)) {
255 m_directions.append(property);
256 propertyFlag |= Property::Direction;
257 }
else if (!(propertyFlag & Property::TimingFunction) && isTimingFunction(property)) {
258 m_timingFunctions.append(property);
259 propertyFlag |= Property::TimingFunction;
260 }
else if (!(propertyFlag & Property::FillMode) && isFillMode(property)) {
261 m_fillModes.append(property);
262 propertyFlag |= Property::FillMode;
263 }
else if (!(propertyFlag & Property::PlayState)&& isPlayState(property)) {
264 m_playStates.append(property);
265 propertyFlag |= Property::PlayState;
267 m_names.append(property);
268 propertyFlag |= Property::Name;
QSvgOffsetProperty offset() const
QSvgCssProperties(const QXmlStreamAttributes &attributes)
QList< QSvgAnimationProperty > animations() const
Combined button and popup list for selecting options.