Changeset View
Standalone View
scripting/scriptedeffect.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org> | 5 | Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org> | ||
6 | Copyright (C) 2018 David Edmundson <davidedmundson@kde.org> | ||||
6 | 7 | | |||
7 | This program is free software; you can redistribute it and/or modify | 8 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | 9 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | 10 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | 11 | (at your option) any later version. | ||
11 | 12 | | |||
12 | This program is distributed in the hope that it will be useful, | 13 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
Show All 12 Lines | |||||
26 | #include "../screenedge.h" | 27 | #include "../screenedge.h" | ||
27 | #include "scripting_logging.h" | 28 | #include "scripting_logging.h" | ||
28 | // KDE | 29 | // KDE | ||
29 | #include <KConfigGroup> | 30 | #include <KConfigGroup> | ||
30 | #include <kconfigloader.h> | 31 | #include <kconfigloader.h> | ||
31 | #include <KPluginMetaData> | 32 | #include <KPluginMetaData> | ||
32 | // Qt | 33 | // Qt | ||
33 | #include <QFile> | 34 | #include <QFile> | ||
34 | #include <QtScript/QScriptEngine> | 35 | #include <QQmlEngine> | ||
35 | #include <QtScript/QScriptValueIterator> | | |||
36 | #include <QStandardPaths> | 36 | #include <QStandardPaths> | ||
37 | 37 | | |||
38 | typedef KWin::EffectWindow* KEffectWindowRef; | 38 | typedef KWin::EffectWindow* KEffectWindowRef; | ||
zzag: I suppose we don't need it anymore. | |||||
39 | 39 | | |||
40 | Q_DECLARE_METATYPE(KSharedConfigPtr) | 40 | Q_DECLARE_METATYPE(KSharedConfigPtr) | ||
41 | 41 | | |||
42 | namespace KWin | 42 | namespace KWin | ||
43 | { | 43 | { | ||
44 | 44 | | |||
45 | QScriptValue kwinEffectScriptPrint(QScriptContext *context, QScriptEngine *engine) | | |||
46 | { | | |||
47 | ScriptedEffect *script = qobject_cast<ScriptedEffect*>(context->callee().data().toQObject()); | | |||
48 | QString result; | | |||
49 | for (int i = 0; i < context->argumentCount(); ++i) { | | |||
50 | if (i > 0) { | | |||
51 | result.append(QLatin1Char(' ')); | | |||
52 | } | | |||
53 | result.append(context->argument(i).toString()); | | |||
54 | } | | |||
55 | qCDebug(KWIN_SCRIPTING) << script->scriptFile() << ":" << result; | | |||
56 | | ||||
57 | return engine->undefinedValue(); | | |||
58 | } | | |||
59 | | ||||
60 | QScriptValue kwinEffectScriptAnimationTime(QScriptContext *context, QScriptEngine *engine) | | |||
61 | { | | |||
62 | if (context->argumentCount() != 1) { | | |||
63 | return engine->undefinedValue(); | | |||
64 | } | | |||
65 | if (!context->argument(0).isNumber()) { | | |||
66 | return engine->undefinedValue(); | | |||
67 | } | | |||
68 | return Effect::animationTime(context->argument(0).toInteger()); | | |||
69 | } | | |||
70 | | ||||
71 | QScriptValue kwinEffectDisplayWidth(QScriptContext *context, QScriptEngine *engine) | | |||
72 | { | | |||
73 | Q_UNUSED(context) | | |||
74 | Q_UNUSED(engine) | | |||
75 | return screens()->displaySize().width(); | | |||
76 | } | | |||
77 | | ||||
78 | QScriptValue kwinEffectDisplayHeight(QScriptContext *context, QScriptEngine *engine) | | |||
79 | { | | |||
80 | Q_UNUSED(context) | | |||
81 | Q_UNUSED(engine) | | |||
82 | return screens()->displaySize().height(); | | |||
83 | } | | |||
84 | | ||||
85 | QScriptValue kwinScriptGlobalShortcut(QScriptContext *context, QScriptEngine *engine) | | |||
86 | { | | |||
87 | return globalShortcut<KWin::ScriptedEffect*>(context, engine); | | |||
88 | } | | |||
89 | | ||||
90 | QScriptValue kwinScriptScreenEdge(QScriptContext *context, QScriptEngine *engine) | | |||
91 | { | | |||
92 | return registerScreenEdge<KWin::ScriptedEffect*>(context, engine); | | |||
93 | } | | |||
94 | | ||||
95 | QScriptValue kwinRegisterTouchScreenEdge(QScriptContext *context, QScriptEngine *engine) | | |||
96 | { | | |||
97 | return registerTouchScreenEdge<KWin::ScriptedEffect*>(context, engine); | | |||
98 | } | | |||
99 | | ||||
100 | QScriptValue kwinUnregisterTouchScreenEdge(QScriptContext *context, QScriptEngine *engine) | | |||
101 | { | | |||
102 | return unregisterTouchScreenEdge<KWin::ScriptedEffect*>(context, engine); | | |||
103 | } | | |||
104 | | ||||
105 | struct AnimationSettings { | 45 | struct AnimationSettings { | ||
106 | enum { Type = 1<<0, Curve = 1<<1, Delay = 1<<2, Duration = 1<<3 }; | 46 | enum { Type = 1<<0, Curve = 1<<1, Delay = 1<<2, Duration = 1<<3 }; | ||
107 | AnimationEffect::Attribute type; | 47 | AnimationEffect::Attribute type; | ||
108 | QEasingCurve::Type curve; | 48 | QEasingCurve::Type curve; | ||
109 | FPx2 from; | 49 | QJSValue from; //should be a KFx2 | ||
110 | FPx2 to; | 50 | QJSValue to; | ||
zzag: Is it a hint? KFx2 => FPx2? | |||||
111 | int delay; | 51 | int delay; | ||
112 | uint duration; | 52 | uint duration; | ||
113 | uint set; | 53 | uint set; | ||
114 | uint metaData; | 54 | uint metaData; | ||
115 | }; | 55 | }; | ||
116 | 56 | | |||
117 | AnimationSettings animationSettingsFromObject(QScriptValue &object) | 57 | AnimationSettings animationSettingsFromObject(const QJSValue &object) | ||
118 | { | 58 | { | ||
119 | AnimationSettings settings; | 59 | AnimationSettings settings; | ||
120 | settings.set = 0; | 60 | settings.set = 0; | ||
121 | settings.metaData = 0; | 61 | settings.metaData = 0; | ||
122 | 62 | | |||
123 | settings.to = qscriptvalue_cast<FPx2>(object.property(QStringLiteral("to"))); | 63 | settings.to = object.property(QStringLiteral("to")); | ||
124 | settings.from = qscriptvalue_cast<FPx2>(object.property(QStringLiteral("from"))); | 64 | settings.from = object.property(QStringLiteral("from")); | ||
125 | 65 | | |||
126 | QScriptValue duration = object.property(QStringLiteral("duration")); | 66 | QJSValue duration = object.property(QStringLiteral("duration")); | ||
127 | if (duration.isValid() && duration.isNumber()) { | 67 | if (duration.isNumber()) { | ||
128 | settings.duration = duration.toUInt32(); | 68 | settings.duration = duration.toUInt(); | ||
129 | settings.set |= AnimationSettings::Duration; | 69 | settings.set |= AnimationSettings::Duration; | ||
130 | } else { | 70 | } else { | ||
131 | settings.duration = 0; | 71 | settings.duration = 0; | ||
132 | } | 72 | } | ||
133 | 73 | | |||
134 | QScriptValue delay = object.property(QStringLiteral("delay")); | 74 | QJSValue delay = object.property(QStringLiteral("delay")); | ||
135 | if (delay.isValid() && delay.isNumber()) { | 75 | if (delay.isNumber()) { | ||
136 | settings.delay = delay.toInt32(); | 76 | settings.delay = delay.toInt(); | ||
137 | settings.set |= AnimationSettings::Delay; | 77 | settings.set |= AnimationSettings::Delay; | ||
138 | } else { | 78 | } else { | ||
139 | settings.delay = 0; | 79 | settings.delay = 0; | ||
140 | } | 80 | } | ||
141 | 81 | | |||
142 | QScriptValue curve = object.property(QStringLiteral("curve")); | 82 | QJSValue curve = object.property(QStringLiteral("curve")); | ||
143 | if (curve.isValid() && curve.isNumber()) { | 83 | if (curve.isNumber()) { | ||
144 | settings.curve = static_cast<QEasingCurve::Type>(curve.toInt32()); | 84 | settings.curve = static_cast<QEasingCurve::Type>(curve.toInt()); | ||
145 | settings.set |= AnimationSettings::Curve; | 85 | settings.set |= AnimationSettings::Curve; | ||
146 | } else { | 86 | } else { | ||
147 | settings.curve = QEasingCurve::Linear; | 87 | settings.curve = QEasingCurve::Linear; | ||
148 | } | 88 | } | ||
149 | 89 | | |||
150 | QScriptValue type = object.property(QStringLiteral("type")); | 90 | QJSValue type = object.property(QStringLiteral("type")); | ||
151 | if (type.isValid() && type.isNumber()) { | 91 | if (type.isNumber()) { | ||
152 | settings.type = static_cast<AnimationEffect::Attribute>(type.toInt32()); | 92 | settings.type = static_cast<AnimationEffect::Attribute>(type.toInt()); | ||
153 | settings.set |= AnimationSettings::Type; | 93 | settings.set |= AnimationSettings::Type; | ||
154 | } else { | 94 | } else { | ||
155 | settings.type = static_cast<AnimationEffect::Attribute>(-1); | 95 | settings.type = static_cast<AnimationEffect::Attribute>(-1); | ||
156 | } | 96 | } | ||
157 | | ||||
158 | return settings; | | |||
159 | } | | |||
160 | | ||||
161 | QList<AnimationSettings> animationSettings(QScriptContext *context, ScriptedEffect *effect, EffectWindow **window) | | |||
162 | { | | |||
163 | QList<AnimationSettings> settings; | | |||
164 | if (!effect) { | | |||
165 | context->throwError(QScriptContext::ReferenceError, QStringLiteral("Internal Scripted KWin Effect error")); | | |||
166 | return settings; | | |||
167 | } | | |||
168 | if (context->argumentCount() != 1) { | | |||
169 | context->throwError(QScriptContext::SyntaxError, QStringLiteral("Exactly one argument expected")); | | |||
170 | return settings; | | |||
171 | } | | |||
172 | if (!context->argument(0).isObject()) { | | |||
173 | context->throwError(QScriptContext::TypeError, QStringLiteral("Argument needs to be an object")); | | |||
174 | return settings; | | |||
175 | } | | |||
176 | QScriptValue object = context->argument(0); | | |||
177 | QScriptValue windowProperty = object.property(QStringLiteral("window")); | | |||
178 | if (!windowProperty.isValid() || !windowProperty.isObject()) { | | |||
179 | context->throwError(QScriptContext::TypeError, QStringLiteral("Window property missing in animation options")); | | |||
180 | return settings; | | |||
181 | } | | |||
182 | *window = qobject_cast<EffectWindow*>(windowProperty.toQObject()); | | |||
183 | | ||||
184 | settings << animationSettingsFromObject(object); // global | | |||
185 | | ||||
186 | QScriptValue animations = object.property(QStringLiteral("animations")); // array | | |||
187 | if (animations.isValid()) { | | |||
188 | if (!animations.isArray()) { | | |||
189 | context->throwError(QScriptContext::TypeError, QStringLiteral("Animations provided but not an array")); | | |||
190 | settings.clear(); | | |||
191 | return settings; | | |||
192 | } | | |||
193 | const int length = static_cast<int>(animations.property(QStringLiteral("length")).toInteger()); | | |||
194 | for (int i=0; i<length; ++i) { | | |||
195 | QScriptValue value = animations.property(QString::number(i)); | | |||
196 | if (!value.isValid()) { | | |||
197 | continue; | | |||
198 | } | | |||
199 | if (value.isObject()) { | | |||
200 | AnimationSettings s = animationSettingsFromObject(value); | | |||
201 | const uint set = s.set | settings.at(0).set; | | |||
202 | // Catch show stoppers (incompletable animation) | | |||
203 | if (!(set & AnimationSettings::Type)) { | | |||
204 | context->throwError(QScriptContext::TypeError, QStringLiteral("Type property missing in animation options")); | | |||
205 | continue; | | |||
206 | } | | |||
207 | if (!(set & AnimationSettings::Duration)) { | | |||
208 | context->throwError(QScriptContext::TypeError, QStringLiteral("Duration property missing in animation options")); | | |||
209 | continue; | | |||
210 | } | | |||
211 | // Complete local animations from global settings | | |||
212 | if (!(s.set & AnimationSettings::Duration)) { | | |||
213 | s.duration = settings.at(0).duration; | | |||
214 | } | | |||
215 | if (!(s.set & AnimationSettings::Curve)) { | | |||
216 | s.curve = settings.at(0).curve; | | |||
217 | } | | |||
218 | if (!(s.set & AnimationSettings::Delay)) { | | |||
219 | s.delay = settings.at(0).delay; | | |||
220 | } | | |||
221 | | ||||
222 | s.metaData = 0; | | |||
223 | typedef QMap<AnimationEffect::MetaType, QString> MetaTypeMap; | | |||
224 | static MetaTypeMap metaTypes({ | | |||
225 | {AnimationEffect::SourceAnchor, QStringLiteral("sourceAnchor")}, | | |||
226 | {AnimationEffect::TargetAnchor, QStringLiteral("targetAnchor")}, | | |||
227 | {AnimationEffect::RelativeSourceX, QStringLiteral("relativeSourceX")}, | | |||
228 | {AnimationEffect::RelativeSourceY, QStringLiteral("relativeSourceY")}, | | |||
229 | {AnimationEffect::RelativeTargetX, QStringLiteral("relativeTargetX")}, | | |||
230 | {AnimationEffect::RelativeTargetY, QStringLiteral("relativeTargetY")}, | | |||
231 | {AnimationEffect::Axis, QStringLiteral("axis")} | | |||
232 | }); | | |||
233 | | ||||
234 | for (MetaTypeMap::const_iterator it = metaTypes.constBegin(), | | |||
235 | end = metaTypes.constEnd(); it != end; ++it) { | | |||
236 | QScriptValue metaVal = value.property(*it); | | |||
237 | if (metaVal.isValid() && metaVal.isNumber()) { | | |||
238 | AnimationEffect::setMetaData(it.key(), metaVal.toInt32(), s.metaData); | | |||
239 | } | | |||
240 | } | | |||
241 | | ||||
242 | settings << s; | | |||
243 | } | | |||
244 | } | | |||
245 | } | | |||
246 | | ||||
247 | if (settings.count() == 1) { | | |||
248 | const uint set = settings.at(0).set; | | |||
249 | if (!(set & AnimationSettings::Type)) { | | |||
250 | context->throwError(QScriptContext::TypeError, QStringLiteral("Type property missing in animation options")); | | |||
251 | settings.clear(); | | |||
252 | } | | |||
253 | if (!(set & AnimationSettings::Duration)) { | | |||
254 | context->throwError(QScriptContext::TypeError, QStringLiteral("Duration property missing in animation options")); | | |||
255 | settings.clear(); | | |||
256 | } | | |||
257 | } else if (!(settings.at(0).set & AnimationSettings::Type)) { // invalid global | | |||
258 | settings.removeAt(0); // -> get rid of it, only used to complete the others | | |||
259 | } | | |||
260 | | ||||
261 | return settings; | 97 | return settings; | ||
262 | } | 98 | } | ||
263 | 99 | | |||
264 | QScriptValue kwinEffectAnimate(QScriptContext *context, QScriptEngine *engine) | 100 | KWin::FPx2 fpx2FromScriptValue(const QJSValue &value) | ||
265 | { | | |||
266 | ScriptedEffect *effect = qobject_cast<ScriptedEffect*>(context->callee().data().toQObject()); | | |||
267 | EffectWindow *window; | | |||
268 | QList<AnimationSettings> settings = animationSettings(context, effect, &window); | | |||
269 | if (settings.empty()) { | | |||
270 | context->throwError(QScriptContext::TypeError, QStringLiteral("No animations provided")); | | |||
271 | return engine->undefinedValue(); | | |||
272 | } | | |||
273 | if (!window) { | | |||
274 | context->throwError(QScriptContext::TypeError, QStringLiteral("Window property does not contain an EffectWindow")); | | |||
275 | return engine->undefinedValue(); | | |||
276 | } | | |||
277 | | ||||
278 | QScriptValue array = engine->newArray(settings.length()); | | |||
279 | int i = 0; | | |||
280 | foreach (const AnimationSettings &setting, settings) { | | |||
281 | array.setProperty(i, (uint)effect->animate(window, | | |||
282 | setting.type, | | |||
283 | setting.duration, | | |||
284 | setting.to, | | |||
285 | setting.from, | | |||
286 | setting.metaData, | | |||
287 | setting.curve, | | |||
288 | setting.delay)); | | |||
289 | ++i; | | |||
290 | } | | |||
291 | return array; | | |||
292 | } | | |||
293 | | ||||
294 | QScriptValue kwinEffectSet(QScriptContext *context, QScriptEngine *engine) | | |||
295 | { | | |||
296 | ScriptedEffect *effect = qobject_cast<ScriptedEffect*>(context->callee().data().toQObject()); | | |||
297 | | ||||
298 | EffectWindow *window; | | |||
299 | QList<AnimationSettings> settings = animationSettings(context, effect, &window); | | |||
300 | if (settings.empty()) { | | |||
301 | context->throwError(QScriptContext::TypeError, QStringLiteral("No animations provided")); | | |||
302 | return engine->undefinedValue(); | | |||
303 | } | | |||
304 | if (!window) { | | |||
305 | context->throwError(QScriptContext::TypeError, QStringLiteral("Window property does not contain an EffectWindow")); | | |||
306 | return engine->undefinedValue(); | | |||
307 | } | | |||
308 | | ||||
309 | QList<QVariant> animIds; | | |||
310 | foreach (const AnimationSettings &setting, settings) { | | |||
311 | animIds << QVariant(effect->set(window, | | |||
312 | setting.type, | | |||
313 | setting.duration, | | |||
314 | setting.to, | | |||
315 | setting.from, | | |||
316 | setting.metaData, | | |||
317 | setting.curve, | | |||
318 | setting.delay)); | | |||
319 | } | | |||
320 | | ||||
321 | return engine->newVariant(animIds); | | |||
322 | } | | |||
323 | | ||||
324 | QList<quint64> animations(const QVariant &v, bool *ok) | | |||
325 | { | | |||
326 | QList<quint64> animIds; | | |||
327 | *ok = false; | | |||
328 | if (v.isValid()) { | | |||
329 | quint64 animId = v.toULongLong(ok); | | |||
330 | if (*ok) | | |||
331 | animIds << animId; | | |||
332 | } | | |||
333 | if (!*ok) { // may still be a variantlist of variants being quint64 | | |||
334 | QList<QVariant> list = v.toList(); | | |||
335 | if (!list.isEmpty()) { | | |||
336 | foreach (const QVariant &vv, list) { | | |||
337 | quint64 animId = vv.toULongLong(ok); | | |||
338 | if (*ok) | | |||
339 | animIds << animId; | | |||
340 | } | | |||
341 | *ok = !animIds.isEmpty(); | | |||
342 | } | | |||
343 | } | | |||
344 | return animIds; | | |||
345 | } | | |||
346 | | ||||
347 | QScriptValue fpx2ToScriptValue(QScriptEngine *eng, const KWin::FPx2 &fpx2) | | |||
348 | { | | |||
349 | QScriptValue val = eng->newObject(); | | |||
350 | val.setProperty(QStringLiteral("value1"), fpx2[0]); | | |||
351 | val.setProperty(QStringLiteral("value2"), fpx2[1]); | | |||
352 | return val; | | |||
353 | } | | |||
354 | | ||||
355 | void fpx2FromScriptValue(const QScriptValue &value, KWin::FPx2 &fpx2) | | |||
356 | { | 101 | { | ||
357 | if (value.isNull()) { | 102 | if (value.isNull()) { | ||
358 | fpx2 = FPx2(); | 103 | return FPx2(); | ||
359 | return; | | |||
360 | } | 104 | } | ||
361 | if (value.isNumber()) { | 105 | if (value.isNumber()) { | ||
362 | fpx2 = FPx2(value.toNumber()); | 106 | return FPx2(value.toNumber()); | ||
363 | return; | | |||
364 | } | 107 | } | ||
365 | if (value.isObject()) { | 108 | if (value.isObject()) { | ||
366 | QScriptValue value1 = value.property(QStringLiteral("value1")); | 109 | QJSValue value1 = value.property(QStringLiteral("value1")); | ||
367 | QScriptValue value2 = value.property(QStringLiteral("value2")); | 110 | QJSValue value2 = value.property(QStringLiteral("value2")); | ||
368 | if (!value1.isValid() || !value2.isValid() || !value1.isNumber() || !value2.isNumber()) { | 111 | if (!value1.isNumber() || !value2.isNumber()) { | ||
369 | qCDebug(KWIN_SCRIPTING) << "Cannot cast scripted FPx2 to C++"; | 112 | qCDebug(KWIN_SCRIPTING) << "Cannot cast scripted FPx2 to C++"; | ||
370 | fpx2 = FPx2(); | 113 | return FPx2(); | ||
371 | return; | | |||
372 | } | 114 | } | ||
373 | fpx2 = FPx2(value1.toNumber(), value2.toNumber()); | 115 | return FPx2(value1.toNumber(), value2.toNumber()); | ||
374 | } | 116 | } | ||
375 | } | 117 | return FPx2(); | ||
376 | | ||||
377 | QScriptValue kwinEffectRetarget(QScriptContext *context, QScriptEngine *engine) | | |||
378 | { | | |||
379 | ScriptedEffect *effect = qobject_cast<ScriptedEffect*>(context->callee().data().toQObject()); | | |||
380 | if (context->argumentCount() < 2 || context->argumentCount() > 3) { | | |||
381 | context->throwError(QScriptContext::SyntaxError, QStringLiteral("2 or 3 arguments expected")); | | |||
382 | return engine->undefinedValue(); | | |||
383 | } | | |||
384 | QVariant v = context->argument(0).toVariant(); | | |||
385 | bool ok = false; | | |||
386 | QList<quint64> animIds = animations(v, &ok); | | |||
387 | if (!ok) { | | |||
388 | context->throwError(QScriptContext::TypeError, QStringLiteral("Argument needs to be one or several quint64")); | | |||
389 | return engine->undefinedValue(); | | |||
390 | } | | |||
391 | FPx2 target; | | |||
392 | fpx2FromScriptValue(context->argument(1), target); | | |||
393 | | ||||
394 | ok = false; | | |||
395 | const int remainingTime = context->argumentCount() == 3 ? context->argument(2).toVariant().toInt() : -1; | | |||
396 | foreach (const quint64 &animId, animIds) { | | |||
397 | ok = effect->retarget(animId, target, remainingTime); | | |||
398 | if (!ok) { | | |||
399 | break; | | |||
400 | } | | |||
401 | } | | |||
402 | | ||||
403 | return QScriptValue(ok); | | |||
404 | } | | |||
405 | | ||||
406 | QScriptValue kwinEffectCancel(QScriptContext *context, QScriptEngine *engine) | | |||
407 | { | | |||
408 | ScriptedEffect *effect = qobject_cast<ScriptedEffect*>(context->callee().data().toQObject()); | | |||
409 | if (context->argumentCount() != 1) { | | |||
410 | context->throwError(QScriptContext::SyntaxError, QStringLiteral("Exactly one argument expected")); | | |||
411 | return engine->undefinedValue(); | | |||
412 | } | | |||
413 | QVariant v = context->argument(0).toVariant(); | | |||
414 | bool ok = false; | | |||
415 | QList<quint64> animIds = animations(v, &ok); | | |||
416 | if (!ok) { | | |||
417 | context->throwError(QScriptContext::TypeError, QStringLiteral("Argument needs to be one or several quint64")); | | |||
418 | return engine->undefinedValue(); | | |||
419 | } | | |||
420 | foreach (const quint64 &animId, animIds) { | | |||
421 | ok |= engine->newVariant(effect->cancel(animId)).toBool(); | | |||
422 | } | | |||
423 | | ||||
424 | return engine->newVariant(ok); | | |||
425 | } | | |||
426 | | ||||
427 | QScriptValue effectWindowToScriptValue(QScriptEngine *eng, const KEffectWindowRef &window) | | |||
428 | { | | |||
429 | return eng->newQObject(window, QScriptEngine::QtOwnership, | | |||
430 | QScriptEngine::ExcludeChildObjects | QScriptEngine::ExcludeDeleteLater | QScriptEngine::PreferExistingWrapperObject); | | |||
431 | } | | |||
432 | | ||||
433 | void effectWindowFromScriptValue(const QScriptValue &value, EffectWindow* &window) | | |||
434 | { | | |||
435 | window = qobject_cast<EffectWindow*>(value.toQObject()); | | |||
436 | } | 118 | } | ||
437 | 119 | | |||
438 | ScriptedEffect *ScriptedEffect::create(const KPluginMetaData &effect) | 120 | ScriptedEffect *ScriptedEffect::create(const KPluginMetaData &effect) | ||
439 | { | 121 | { | ||
440 | const QString name = effect.pluginId(); | 122 | const QString name = effect.pluginId(); | ||
441 | const QString scriptName = effect.value(QStringLiteral("X-Plasma-MainScript")); | 123 | const QString scriptName = effect.value(QStringLiteral("X-Plasma-MainScript")); | ||
442 | if (scriptName.isEmpty()) { | 124 | if (scriptName.isEmpty()) { | ||
443 | qCDebug(KWIN_SCRIPTING) << "X-Plasma-MainScript not set"; | 125 | qCDebug(KWIN_SCRIPTING) << "X-Plasma-MainScript not set"; | ||
Show All 21 Lines | |||||
465 | 147 | | |||
466 | bool ScriptedEffect::supported() | 148 | bool ScriptedEffect::supported() | ||
467 | { | 149 | { | ||
468 | return effects->animationsSupported(); | 150 | return effects->animationsSupported(); | ||
469 | } | 151 | } | ||
470 | 152 | | |||
471 | ScriptedEffect::ScriptedEffect() | 153 | ScriptedEffect::ScriptedEffect() | ||
472 | : AnimationEffect() | 154 | : AnimationEffect() | ||
473 | , m_engine(new QScriptEngine(this)) | 155 | , m_engine(new QJSEngine(this)) | ||
474 | , m_scriptFile(QString()) | 156 | , m_scriptFile(QString()) | ||
475 | , m_config(nullptr) | 157 | , m_config(nullptr) | ||
476 | , m_chainPosition(0) | 158 | , m_chainPosition(0) | ||
477 | { | 159 | { | ||
478 | connect(m_engine, SIGNAL(signalHandlerException(QScriptValue)), SLOT(signalHandlerException(QScriptValue))); | | |||
479 | } | 160 | } | ||
480 | 161 | | |||
481 | ScriptedEffect::~ScriptedEffect() | 162 | ScriptedEffect::~ScriptedEffect() | ||
482 | { | 163 | { | ||
483 | } | 164 | } | ||
484 | 165 | | |||
485 | bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript) | 166 | bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript) | ||
486 | { | 167 | { | ||
168 | qRegisterMetaType<QJSValueList>(); | ||||
487 | QFile scriptFile(pathToScript); | 169 | QFile scriptFile(pathToScript); | ||
488 | if (!scriptFile.open(QIODevice::ReadOnly)) { | 170 | if (!scriptFile.open(QIODevice::ReadOnly)) { | ||
489 | qCDebug(KWIN_SCRIPTING) << "Could not open script file: " << pathToScript; | 171 | qCDebug(KWIN_SCRIPTING) << "Could not open script file: " << pathToScript; | ||
490 | return false; | 172 | return false; | ||
491 | } | 173 | } | ||
492 | m_effectName = effectName; | 174 | m_effectName = effectName; | ||
493 | m_scriptFile = pathToScript; | 175 | m_scriptFile = pathToScript; | ||
494 | 176 | | |||
495 | // does the effect contain an KConfigXT file? | 177 | // does the effect contain an KConfigXT file? | ||
496 | const QString kconfigXTFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(KWIN_NAME "/effects/") + m_effectName + QLatin1String("/contents/config/main.xml")); | 178 | const QString kconfigXTFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(KWIN_NAME "/effects/") + m_effectName + QLatin1String("/contents/config/main.xml")); | ||
497 | if (!kconfigXTFile.isNull()) { | 179 | if (!kconfigXTFile.isNull()) { | ||
498 | KConfigGroup cg = QCoreApplication::instance()->property("config").value<KSharedConfigPtr>()->group(QStringLiteral("Effect-%1").arg(m_effectName)); | 180 | KConfigGroup cg = QCoreApplication::instance()->property("config").value<KSharedConfigPtr>()->group(QStringLiteral("Effect-%1").arg(m_effectName)); | ||
499 | QFile xmlFile(kconfigXTFile); | 181 | QFile xmlFile(kconfigXTFile); | ||
500 | m_config = new KConfigLoader(cg, &xmlFile, this); | 182 | m_config = new KConfigLoader(cg, &xmlFile, this); | ||
501 | m_config->load(); | 183 | m_config->load(); | ||
502 | } | 184 | } | ||
503 | 185 | | |||
504 | QScriptValue effectsObject = m_engine->newQObject(effects, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); | 186 | QJSValue effectsObject = m_engine->newQObject(effects); | ||
505 | m_engine->globalObject().setProperty(QStringLiteral("effects"), effectsObject, QScriptValue::Undeletable); | 187 | QQmlEngine::setObjectOwnership(effects, QQmlEngine::CppOwnership); | ||
188 | m_engine->globalObject().setProperty(QStringLiteral("effects"), effectsObject); | ||||
189 | | ||||
190 | //desktopChanged is overloaded, which is problematic | ||||
191 | //old code exposed the signal also with parameters. QJSEngine does not so we have to fake it | ||||
192 | effectsObject.setProperty("desktopChanged(int,int)", effectsObject.property("desktopChangedCompat")); | ||||
193 | effectsObject.setProperty("desktopChanged", effectsObject.property("desktopChangedCompat")); | ||||
zzag: Is it correct? The third argument has to be a window. | |||||
194 | | ||||
195 | QJSValue selfWrapper = m_engine->newQObject(this); | ||||
196 | QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); | ||||
197 | | ||||
506 | m_engine->globalObject().setProperty(QStringLiteral("Effect"), m_engine->newQMetaObject(&ScriptedEffect::staticMetaObject)); | 198 | m_engine->globalObject().setProperty(QStringLiteral("Effect"), m_engine->newQMetaObject(&ScriptedEffect::staticMetaObject)); | ||
507 | #ifndef KWIN_UNIT_TEST | 199 | #ifndef KWIN_UNIT_TEST | ||
508 | m_engine->globalObject().setProperty(QStringLiteral("KWin"), m_engine->newQMetaObject(&QtScriptWorkspaceWrapper::staticMetaObject)); | 200 | m_engine->globalObject().setProperty(QStringLiteral("KWin"), m_engine->newQMetaObject(&QtScriptWorkspaceWrapper::staticMetaObject)); | ||
509 | #endif | 201 | #endif | ||
202 | | ||||
510 | m_engine->globalObject().setProperty(QStringLiteral("QEasingCurve"), m_engine->newQMetaObject(&QEasingCurve::staticMetaObject)); | 203 | m_engine->globalObject().setProperty(QStringLiteral("QEasingCurve"), m_engine->newQMetaObject(&QEasingCurve::staticMetaObject)); | ||
511 | m_engine->globalObject().setProperty(QStringLiteral("effect"), m_engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater), QScriptValue::Undeletable); | | |||
512 | MetaScripting::registration(m_engine); | | |||
513 | qScriptRegisterMetaType<KEffectWindowRef>(m_engine, effectWindowToScriptValue, effectWindowFromScriptValue); | | |||
514 | qScriptRegisterMetaType<KWin::FPx2>(m_engine, fpx2ToScriptValue, fpx2FromScriptValue); | | |||
515 | qScriptRegisterSequenceMetaType<QList< KWin::EffectWindow* > >(m_engine); | | |||
516 | // add our print | | |||
517 | QScriptValue printFunc = m_engine->newFunction(kwinEffectScriptPrint); | | |||
518 | printFunc.setData(m_engine->newQObject(this)); | | |||
519 | m_engine->globalObject().setProperty(QStringLiteral("print"), printFunc); | | |||
520 | // add our animationTime | | |||
521 | QScriptValue animationTimeFunc = m_engine->newFunction(kwinEffectScriptAnimationTime); | | |||
522 | animationTimeFunc.setData(m_engine->newQObject(this)); | | |||
523 | m_engine->globalObject().setProperty(QStringLiteral("animationTime"), animationTimeFunc); | | |||
524 | // add displayWidth and displayHeight | | |||
525 | QScriptValue displayWidthFunc = m_engine->newFunction(kwinEffectDisplayWidth); | | |||
526 | m_engine->globalObject().setProperty(QStringLiteral("displayWidth"), displayWidthFunc); | | |||
527 | QScriptValue displayHeightFunc = m_engine->newFunction(kwinEffectDisplayHeight); | | |||
528 | m_engine->globalObject().setProperty(QStringLiteral("displayHeight"), displayHeightFunc); | | |||
529 | // add global Shortcut | | |||
530 | registerGlobalShortcutFunction(this, m_engine, kwinScriptGlobalShortcut); | | |||
531 | registerScreenEdgeFunction(this, m_engine, kwinScriptScreenEdge); | | |||
532 | registerTouchScreenEdgeFunction(this, m_engine, kwinRegisterTouchScreenEdge); | | |||
533 | unregisterTouchScreenEdgeFunction(this, m_engine, kwinUnregisterTouchScreenEdge); | | |||
534 | // add the animate method | | |||
535 | QScriptValue animateFunc = m_engine->newFunction(kwinEffectAnimate); | | |||
536 | animateFunc.setData(m_engine->newQObject(this)); | | |||
537 | m_engine->globalObject().setProperty(QStringLiteral("animate"), animateFunc); | | |||
538 | | ||||
539 | // and the set variant | | |||
540 | QScriptValue setFunc = m_engine->newFunction(kwinEffectSet); | | |||
541 | setFunc.setData(m_engine->newQObject(this)); | | |||
542 | m_engine->globalObject().setProperty(QStringLiteral("set"), setFunc); | | |||
543 | | ||||
544 | // retarget | | |||
545 | QScriptValue retargetFunc = m_engine->newFunction(kwinEffectRetarget); | | |||
546 | retargetFunc.setData(m_engine->newQObject(this)); | | |||
547 | m_engine->globalObject().setProperty(QStringLiteral("retarget"), retargetFunc); | | |||
548 | | ||||
549 | // cancel... | | |||
550 | QScriptValue cancelFunc = m_engine->newFunction(kwinEffectCancel); | | |||
551 | cancelFunc.setData(m_engine->newQObject(this)); | | |||
552 | m_engine->globalObject().setProperty(QStringLiteral("cancel"), cancelFunc); | | |||
553 | 204 | | |||
554 | QScriptValue ret = m_engine->evaluate(QString::fromUtf8(scriptFile.readAll())); | 205 | m_engine->globalObject().setProperty("effect", selfWrapper); | ||
206 | | ||||
207 | //expose functions at the root level for compatibility | ||||
208 | engine()->globalObject().setProperty("displayWidth", selfWrapper.property("displayWidth")); | ||||
zzag: IMHO, for the sake of consistency, we have to use either m_engine or engine(). | |||||
209 | engine()->globalObject().setProperty("displayHeight", selfWrapper.property("displayHeight")); | ||||
210 | engine()->globalObject().setProperty("animationTime", selfWrapper.property("animationTime")); | ||||
211 | | ||||
212 | engine()->globalObject().setProperty("registerShortcut", selfWrapper.property("registerShortcut")); | ||||
213 | engine()->globalObject().setProperty("registerScreenEdge", selfWrapper.property("registerScreenEdge")); | ||||
214 | engine()->globalObject().setProperty("unregisterScreenEdge", selfWrapper.property("unregisterScreenEdge")); | ||||
215 | engine()->globalObject().setProperty("registerTouchScreenEdge", selfWrapper.property("registerTouchScreenEdge")); | ||||
216 | engine()->globalObject().setProperty("unregisterTouchScreenEdge", selfWrapper.property("unregisterTouchScreenEdge")); | ||||
217 | | ||||
218 | engine()->globalObject().setProperty("animate", selfWrapper.property("animate")); | ||||
219 | engine()->globalObject().setProperty("set", selfWrapper.property("set")); | ||||
220 | engine()->globalObject().setProperty("retarget", selfWrapper.property("retarget")); | ||||
221 | engine()->globalObject().setProperty("cancel", selfWrapper.property("cancel")); | ||||
222 | | ||||
223 | engine()->globalObject().setProperty("print", selfWrapper.property("print")); | ||||
224 | | ||||
225 | QJSValue ret = m_engine->evaluate(QString::fromUtf8(scriptFile.readAll())); | ||||
555 | 226 | | |||
556 | if (ret.isError()) { | 227 | if (ret.isError()) { | ||
557 | signalHandlerException(ret); | 228 | qCDebug(KWIN_SCRIPTING) << "KWin Effect script encountered an error at [Line " << ret.property("lineNumber").toString() << "]"; | ||
229 | qCDebug(KWIN_SCRIPTING) << "Message: " << ret.property("message").toString(); | ||||
230 | qCDebug(KWIN_SCRIPTING) << ": " << ret.toString(); | ||||
zzag: Should it really be a qCDebug? Maybe qCCritical? | |||||
The old code was a qcdebug. I remember trying to change it once in the past and I had that change blocked. davidedmundson: The old code was a qcdebug.
I remember trying to change it once in the past and I had that… | |||||
558 | return false; | 231 | return false; | ||
559 | } | 232 | } | ||
560 | scriptFile.close(); | 233 | scriptFile.close(); | ||
561 | return true; | 234 | return true; | ||
562 | } | 235 | } | ||
563 | 236 | | |||
564 | void ScriptedEffect::animationEnded(KWin::EffectWindow *w, Attribute a, uint meta) | 237 | void ScriptedEffect::print(const QStringList &parts) | ||
565 | { | 238 | { | ||
566 | AnimationEffect::animationEnded(w, a, meta); | 239 | auto d = qDebug(); | ||
567 | emit animationEnded(w, 0); | 240 | d << scriptFile() << ":"; | ||
241 | for(const QString &part: parts) { | ||||
242 | if (part.isNull()) { | ||||
243 | continue; | ||||
244 | } | ||||
245 | d << part; | ||||
246 | } | ||||
568 | } | 247 | } | ||
What about ConsoleExtension(http://doc.qt.io/qt-5/qjsengine.html#Extension-enum)? Can it be used? zzag: What about ConsoleExtension(http://doc.qt.io/qt-5/qjsengine.html#Extension-enum)? Can it be… | |||||
davidedmundson: good idea, seems to work. | |||||
569 | 248 | | |||
570 | void ScriptedEffect::signalHandlerException(const QScriptValue &value) | 249 | int ScriptedEffect::displayHeight() const | ||
571 | { | 250 | { | ||
572 | if (value.isError()) { | 251 | return screens()->displaySize().height(); | ||
573 | qCDebug(KWIN_SCRIPTING) << "KWin Effect script encountered an error at [Line " << m_engine->uncaughtExceptionLineNumber() << "]"; | 252 | } | ||
574 | qCDebug(KWIN_SCRIPTING) << "Message: " << value.toString(); | | |||
575 | 253 | | |||
576 | QScriptValueIterator iter(value); | 254 | int ScriptedEffect::animationTime(int defaultTime) const | ||
577 | while (iter.hasNext()) { | 255 | { | ||
578 | iter.next(); | 256 | return Effect::animationTime(defaultTime); | ||
579 | qCDebug(KWIN_SCRIPTING) << " " << iter.name() << ": " << iter.value().toString(); | | |||
580 | } | 257 | } | ||
258 | | ||||
259 | int ScriptedEffect::displayWidth() const | ||||
260 | { | ||||
261 | return screens()->displaySize().width(); | ||||
581 | } | 262 | } | ||
263 | | ||||
264 | void ScriptedEffect::animationEnded(KWin::EffectWindow *w, Attribute a, uint meta) | ||||
265 | { | ||||
266 | AnimationEffect::animationEnded(w, a, meta); | ||||
267 | emit animationEnded(w, 0); | ||||
582 | } | 268 | } | ||
583 | 269 | | |||
584 | quint64 ScriptedEffect::animate(KWin::EffectWindow* w, KWin::AnimationEffect::Attribute a, int ms, KWin::FPx2 to, KWin::FPx2 from, uint metaData, QEasingCurve::Type curve, int delay) | 270 | int ScriptedEffect::animate(KWin::EffectWindow* w, KWin::AnimationEffect::Attribute a, int ms, const QJSValue &to, const QJSValue &from, uint metaData, QEasingCurve::Type curve, int delay) | ||
585 | { | 271 | { | ||
586 | QEasingCurve qec; | 272 | QEasingCurve qec; | ||
587 | if (curve < QEasingCurve::Custom) | 273 | if (curve < QEasingCurve::Custom) | ||
588 | qec.setType(curve); | 274 | qec.setType(curve); | ||
589 | else if (static_cast<int>(curve) == static_cast<int>(GaussianCurve)) | 275 | else if (static_cast<int>(curve) == static_cast<int>(GaussianCurve)) | ||
590 | qec.setCustomType(qecGaussian); | 276 | qec.setCustomType(qecGaussian); | ||
591 | return AnimationEffect::animate(w, a, metaData, ms, to, qec, delay, from); | 277 | return AnimationEffect::animate(w, a, metaData, ms, fpx2FromScriptValue(to), qec, delay, fpx2FromScriptValue(from)); | ||
592 | } | 278 | } | ||
593 | 279 | | |||
594 | quint64 ScriptedEffect::set(KWin::EffectWindow* w, KWin::AnimationEffect::Attribute a, int ms, KWin::FPx2 to, KWin::FPx2 from, uint metaData, QEasingCurve::Type curve, int delay) | 280 | int ScriptedEffect::set(KWin::EffectWindow* w, KWin::AnimationEffect::Attribute a, int ms, const QJSValue &to, const QJSValue &from, uint metaData, QEasingCurve::Type curve, int delay) | ||
595 | { | 281 | { | ||
596 | QEasingCurve qec; | 282 | QEasingCurve qec; | ||
597 | if (curve < QEasingCurve::Custom) | 283 | if (curve < QEasingCurve::Custom) | ||
598 | qec.setType(curve); | 284 | qec.setType(curve); | ||
599 | else if (static_cast<int>(curve) == static_cast<int>(GaussianCurve)) | 285 | else if (static_cast<int>(curve) == static_cast<int>(GaussianCurve)) | ||
600 | qec.setCustomType(qecGaussian); | 286 | qec.setCustomType(qecGaussian); | ||
601 | return AnimationEffect::set(w, a, metaData, ms, to, qec, delay, from); | 287 | return AnimationEffect::set(w, a, metaData, ms, fpx2FromScriptValue(to), qec, delay, fpx2FromScriptValue(from)); | ||
288 | } | ||||
289 | | ||||
290 | bool ScriptedEffect::retarget(int animationId, const QJSValue &newTarget, int newRemainingTime) | ||||
291 | { | ||||
292 | return AnimationEffect::retarget(animationId, fpx2FromScriptValue(newTarget), newRemainingTime); | ||||
602 | } | 293 | } | ||
603 | 294 | | |||
604 | bool ScriptedEffect::retarget(quint64 animationId, KWin::FPx2 newTarget, int newRemainingTime) | 295 | bool ScriptedEffect::retarget(QList<int> animationIds, const QJSValue &newTarget, int newRemainingTime) | ||
605 | { | 296 | { | ||
606 | return AnimationEffect::retarget(animationId, newTarget, newRemainingTime); | 297 | bool ok = true; | ||
298 | for(int animationId: animationIds) { | ||||
Don't need qAsConst, animationIds is already const. Also, maybe const int &animationId. zzag: Don't need qAsConst, animationIds is already const. Also, maybe `const int &animationId`. | |||||
299 | ok |= retarget(animationId, newTarget, newRemainingTime); | ||||
300 | } | ||||
301 | return true; | ||||
607 | } | 302 | } | ||
608 | 303 | | |||
304 | QJSValue ScriptedEffect::animate(const QJSValue &args) | ||||
305 | { | ||||
306 | return startAnimation(args, false); | ||||
307 | } | ||||
308 | | ||||
309 | QJSValue ScriptedEffect::set(const QJSValue &object) | ||||
310 | { | ||||
311 | return startAnimation(object, true); | ||||
312 | } | ||||
313 | | ||||
314 | QJSValue ScriptedEffect::createError(const QString &errorMessage) { | ||||
315 | return m_engine->evaluate(QString("new Error('%1');").arg(errorMessage)); | ||||
zzag: QStringLiteral | |||||
316 | } | ||||
317 | | ||||
318 | QJSValue ScriptedEffect::startAnimation(const QJSValue &object, bool settingPersists) | ||||
zzag: Can we use an enum instead of the boolean trap? | |||||
319 | { | ||||
320 | QVector<AnimationSettings> settings; | ||||
321 | QJSValue windowProperty = object.property(QStringLiteral("window")); | ||||
322 | if (!windowProperty.isObject()) { | ||||
323 | return createError(QStringLiteral("Window property missing in animation options")); | ||||
324 | } | ||||
325 | auto window = qobject_cast<EffectWindow*>(windowProperty.toQObject()); | ||||
326 | if (!window) { | ||||
327 | return createError(QStringLiteral("Window property references invalid window")); | ||||
328 | } | ||||
329 | | ||||
330 | settings << animationSettingsFromObject(object); // global | ||||
331 | | ||||
332 | QJSValue animations = object.property(QStringLiteral("animations")); // array | ||||
333 | if (!animations.isNull()) { | ||||
334 | if (!animations.isArray()) { | ||||
335 | return createError(QStringLiteral("Animations provided but not an array")); | ||||
336 | } | ||||
337 | const int length = static_cast<int>(animations.property(QStringLiteral("length")).toInt()); | ||||
338 | for (int i=0; i<length; ++i) { | ||||
339 | QJSValue value = animations.property(QString::number(i)); | ||||
340 | if (value.isObject()) { | ||||
341 | AnimationSettings s = animationSettingsFromObject(value); | ||||
342 | const uint set = s.set | settings.at(0).set; | ||||
343 | // Catch show stoppers (incompletable animation) | ||||
344 | if (!(set & AnimationSettings::Type)) { | ||||
345 | return createError(QStringLiteral("Type property missing in animation options")); | ||||
346 | } | ||||
347 | if (!(set & AnimationSettings::Duration)) { | ||||
348 | return createError(QStringLiteral("Duration property missing in animation options")); | ||||
349 | } | ||||
350 | // Complete local animations from global settings | ||||
351 | if (!(s.set & AnimationSettings::Duration)) { | ||||
352 | s.duration = settings.at(0).duration; | ||||
353 | } | ||||
354 | if (!(s.set & AnimationSettings::Curve)) { | ||||
355 | s.curve = settings.at(0).curve; | ||||
356 | } | ||||
357 | if (!(s.set & AnimationSettings::Delay)) { | ||||
358 | s.delay = settings.at(0).delay; | ||||
359 | } | ||||
360 | | ||||
361 | s.metaData = 0; | ||||
362 | typedef QMap<AnimationEffect::MetaType, QString> MetaTypeMap; | ||||
363 | static MetaTypeMap metaTypes({ | ||||
364 | {AnimationEffect::SourceAnchor, QStringLiteral("sourceAnchor")}, | ||||
365 | {AnimationEffect::TargetAnchor, QStringLiteral("targetAnchor")}, | ||||
366 | {AnimationEffect::RelativeSourceX, QStringLiteral("relativeSourceX")}, | ||||
367 | {AnimationEffect::RelativeSourceY, QStringLiteral("relativeSourceY")}, | ||||
368 | {AnimationEffect::RelativeTargetX, QStringLiteral("relativeTargetX")}, | ||||
369 | {AnimationEffect::RelativeTargetY, QStringLiteral("relativeTargetY")}, | ||||
370 | {AnimationEffect::Axis, QStringLiteral("axis")} | ||||
371 | }); | ||||
372 | | ||||
373 | for (MetaTypeMap::const_iterator it = metaTypes.constBegin(), | ||||
374 | end = metaTypes.constEnd(); it != end; ++it) { | ||||
375 | QJSValue metaVal = value.property(*it); | ||||
376 | if (metaVal.isNumber()) { | ||||
377 | AnimationEffect::setMetaData(it.key(), metaVal.toInt(), s.metaData); | ||||
378 | } | ||||
379 | } | ||||
380 | | ||||
381 | settings << s; | ||||
382 | } | ||||
383 | } | ||||
384 | } | ||||
385 | | ||||
386 | if (settings.count() == 1) { | ||||
387 | const uint set = settings.at(0).set; | ||||
388 | if (!(set & AnimationSettings::Type)) { | ||||
389 | return createError(QStringLiteral("Type property missing in animation options")); | ||||
390 | } | ||||
391 | if (!(set & AnimationSettings::Duration)) { | ||||
392 | return createError(QStringLiteral("Duration property missing in animation options")); | ||||
393 | } | ||||
394 | } else if (!(settings.at(0).set & AnimationSettings::Type)) { // invalid global | ||||
395 | settings.removeAt(0); // -> get rid of it, only used to complete the others | ||||
396 | } | ||||
397 | | ||||
398 | if (settings.isEmpty()) { | ||||
399 | return createError(QStringLiteral("No animations provided")); | ||||
400 | } | ||||
401 | | ||||
402 | QJSValue array = m_engine->newArray(settings.length()); | ||||
403 | for (int i = 0 ; i < settings.count() ; i++) { | ||||
404 | const AnimationSettings &setting = settings[i]; | ||||
405 | int animationId; | ||||
406 | if (settingPersists) { | ||||
407 | animationId = set(window, | ||||
408 | setting.type, | ||||
409 | setting.duration, | ||||
410 | setting.to, | ||||
411 | setting.from, | ||||
412 | setting.metaData, | ||||
413 | setting.curve, | ||||
414 | setting.delay); | ||||
415 | } else { | ||||
416 | animationId = animate(window, | ||||
417 | setting.type, | ||||
418 | setting.duration, | ||||
419 | setting.to, | ||||
420 | setting.from, | ||||
421 | setting.metaData, | ||||
422 | setting.curve, | ||||
423 | setting.delay); | ||||
424 | } | ||||
425 | array.setProperty(i, animationId); | ||||
426 | } | ||||
427 | return array; | ||||
428 | } | ||||
429 | | ||||
430 | bool ScriptedEffect::cancel(int animationId) | ||||
431 | { | ||||
432 | return AnimationEffect::cancel(animationId); | ||||
433 | } | ||||
zzag: Same here. | |||||
434 | | ||||
435 | bool ScriptedEffect::cancel(QList<int> animationIds) | ||||
436 | { | ||||
This always returns true - is that intentional? I assume this should've been ok &= cancel(...). fvogt: This always returns true - is that intentional? I assume this should've been `ok &= cancel(... | |||||
davidedmundson: heh, old code quit on first error I'll change to that. | |||||
437 | bool ok = true; | ||||
438 | for(int animationId: animationIds) { | ||||
439 | ok |= cancel(animationId); | ||||
440 | } | ||||
441 | return true; | ||||
442 | } | ||||
443 | | ||||
444 | | ||||
609 | bool ScriptedEffect::isGrabbed(EffectWindow* w, ScriptedEffect::DataRole grabRole) | 445 | bool ScriptedEffect::isGrabbed(EffectWindow* w, ScriptedEffect::DataRole grabRole) | ||
610 | { | 446 | { | ||
611 | void *e = w->data(static_cast<KWin::DataRole>(grabRole)).value<void*>(); | 447 | void *e = w->data(static_cast<KWin::DataRole>(grabRole)).value<void*>(); | ||
612 | if (e) { | 448 | if (e) { | ||
613 | return e != this; | 449 | return e != this; | ||
614 | } else { | 450 | } else { | ||
615 | return false; | 451 | return false; | ||
616 | } | 452 | } | ||
617 | } | 453 | } | ||
618 | 454 | | |||
619 | void ScriptedEffect::reconfigure(ReconfigureFlags flags) | 455 | void ScriptedEffect::reconfigure(ReconfigureFlags flags) | ||
620 | { | 456 | { | ||
621 | AnimationEffect::reconfigure(flags); | 457 | AnimationEffect::reconfigure(flags); | ||
622 | if (m_config) { | 458 | if (m_config) { | ||
623 | m_config->read(); | 459 | m_config->read(); | ||
624 | } | 460 | } | ||
625 | emit configChanged(); | 461 | emit configChanged(); | ||
626 | } | 462 | } | ||
627 | 463 | | |||
628 | void ScriptedEffect::registerShortcut(QAction *a, QScriptValue callback) | 464 | bool ScriptedEffect::registerShortcut(const QString &objectName, const QString &text, const QString &keySequence, const QJSValue &callback) | ||
629 | { | 465 | { | ||
630 | m_shortcutCallbacks.insert(a, callback); | 466 | QAction* a = new QAction(this); | ||
631 | connect(a, SIGNAL(triggered(bool)), SLOT(globalShortcutTriggered())); | 467 | a->setObjectName(objectName); | ||
632 | } | 468 | a->setText(text); | ||
633 | 469 | const QKeySequence shortcut = QKeySequence(keySequence); | |||
zzag: Please put whitespace around = and <. | |||||
634 | void ScriptedEffect::globalShortcutTriggered() | 470 | KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << shortcut); | ||
635 | { | 471 | input()->registerShortcut(shortcut, a); | ||
zzag: QAction *a | |||||
636 | callGlobalShortcutCallback<KWin::ScriptedEffect*>(this, sender()); | 472 | | ||
473 | connect(a, &QAction::triggered, this, [this, a, callback]() { | ||||
474 | QJSValue c(callback); | ||||
475 | | ||||
476 | //old QScriptEngine API returned the QAction of the shortcut | ||||
477 | //in the callback args | ||||
478 | //though this isn't documented..so personally I'd just remove it | ||||
479 | QJSValue actionObject = m_engine->newQObject(a); | ||||
480 | QQmlEngine::setObjectOwnership(a, QQmlEngine::CppOwnership); | ||||
481 | c.call(QJSValueList({actionObject})); | ||||
482 | }); | ||||
483 | return true; | ||||
637 | } | 484 | } | ||
638 | 485 | | |||
639 | bool ScriptedEffect::borderActivated(ElectricBorder edge) | 486 | bool ScriptedEffect::borderActivated(ElectricBorder edge) | ||
640 | { | 487 | { | ||
641 | screenEdgeActivated(this, edge); | 488 | auto it = screenEdgeCallbacks().constFind(edge); | ||
489 | if (it != screenEdgeCallbacks().constEnd()) { | ||||
490 | foreach (const QJSValue &value, it.value()) { | ||||
zzag: Why foreach? | |||||
it's a copy paste from scripting.cpp but ported to QJSValue same for the comments in startAnimation which is just moving the old I may as well modernise it in the port though. Will adjust. davidedmundson: it's a copy paste from scripting.cpp but ported to QJSValue
same for the comments in… | |||||
The value() will return a reference to a const list, thus we don't need qAsConst. Also, whitespace between for keyword and (, and before :. zzag: The value() will return a reference to a const list, thus we don't need qAsConst. Also… | |||||
491 | QJSValue callback(value); | ||||
492 | callback.call(); | ||||
493 | } | ||||
494 | } | ||||
I think we don't need MetaTypeMap. Also, FWIW, in order to guarantee that metaTypes is initialized in thread safe manner, compilers add a synchronization primitive https://godbolt.org/g/YeZ3by. It won't impact performance too much in our case, but still, it would be great to do as less as possible. Also, the initializer list is too deep. It would be great to reduce indentation. zzag: I think we don't need MetaTypeMap.
Also, FWIW, in order to guarantee that metaTypes is… | |||||
642 | return true; | 495 | return true; | ||
643 | } | 496 | } | ||
644 | 497 | | |||
645 | QVariant ScriptedEffect::readConfig(const QString &key, const QVariant defaultValue) | 498 | QVariant ScriptedEffect::readConfig(const QString &key, const QVariant defaultValue) | ||
646 | { | 499 | { | ||
647 | if (!m_config) { | 500 | if (!m_config) { | ||
648 | return defaultValue; | 501 | return defaultValue; | ||
649 | } | 502 | } | ||
650 | return m_config->property(key); | 503 | return m_config->property(key); | ||
651 | } | 504 | } | ||
zzag: auto. | |||||
652 | 505 | | |||
653 | bool ScriptedEffect::registerTouchScreenCallback(int edge, QScriptValue callback) | 506 | bool ScriptedEffect::registerScreenEdge(int edge, const QJSValue &callback) | ||
507 | { | ||||
508 | auto it = screenEdgeCallbacks().find(edge); | ||||
509 | if (it == screenEdgeCallbacks().end()) { | ||||
510 | // not yet registered | ||||
511 | ScreenEdges::self()->reserve(static_cast<KWin::ElectricBorder>(edge), this, "borderActivated"); | ||||
512 | screenEdgeCallbacks().insert(edge, QList<QJSValue>() << callback); | ||||
zzag: (edge, {callback}) | |||||
513 | } else { | ||||
514 | it->append(callback); | ||||
515 | } | ||||
516 | return true; | ||||
517 | } | ||||
518 | | ||||
519 | bool ScriptedEffect::unregisterScreenEdge(int edge) | ||||
520 | { | ||||
521 | auto it = screenEdgeCallbacks().find(edge); | ||||
522 | if (it == screenEdgeCallbacks().end()) { | ||||
523 | //not previously registered | ||||
524 | return false; | ||||
525 | } | ||||
526 | ScreenEdges::self()->unreserve(static_cast<KWin::ElectricBorder>(edge), this); | ||||
527 | screenEdgeCallbacks().erase(it); | ||||
528 | return true; | ||||
529 | } | ||||
530 | | ||||
531 | bool ScriptedEffect::registerTouchScreenEdge(int edge, const QJSValue &callback) | ||||
654 | { | 532 | { | ||
655 | if (m_touchScreenEdgeCallbacks.constFind(edge) != m_touchScreenEdgeCallbacks.constEnd()) { | 533 | if (m_touchScreenEdgeCallbacks.constFind(edge) != m_touchScreenEdgeCallbacks.constEnd()) { | ||
656 | return false; | 534 | return false; | ||
zzag: No whitespace before ';'. | |||||
657 | } | 535 | } | ||
658 | QAction *action = new QAction(this); | 536 | QAction *action = new QAction(this); | ||
659 | connect(action, &QAction::triggered, this, | 537 | connect(action, &QAction::triggered, this, | ||
660 | [callback] { | 538 | [callback] { | ||
661 | QScriptValue invoke(callback); | 539 | QJSValue invoke(callback); | ||
662 | invoke.call(); | 540 | invoke.call(); | ||
663 | } | 541 | } | ||
664 | ); | 542 | ); | ||
665 | ScreenEdges::self()->reserveTouch(KWin::ElectricBorder(edge), action); | 543 | ScreenEdges::self()->reserveTouch(KWin::ElectricBorder(edge), action); | ||
666 | m_touchScreenEdgeCallbacks.insert(edge, action); | 544 | m_touchScreenEdgeCallbacks.insert(edge, action); | ||
667 | return true; | 545 | return true; | ||
668 | } | 546 | } | ||
669 | 547 | | |||
670 | bool ScriptedEffect::unregisterTouchScreenCallback(int edge) | 548 | bool ScriptedEffect::unregisterTouchScreenEdge(int edge) | ||
671 | { | 549 | { | ||
672 | auto it = m_touchScreenEdgeCallbacks.find(edge); | 550 | auto it = m_touchScreenEdgeCallbacks.find(edge); | ||
673 | if (it == m_touchScreenEdgeCallbacks.end()) { | 551 | if (it == m_touchScreenEdgeCallbacks.end()) { | ||
674 | return false; | 552 | return false; | ||
675 | } | 553 | } | ||
676 | delete it.value(); | 554 | delete it.value(); | ||
555 | | ||||
zzag: Somewhat unrelated change. | |||||
677 | m_touchScreenEdgeCallbacks.erase(it); | 556 | m_touchScreenEdgeCallbacks.erase(it); | ||
678 | return true; | 557 | return true; | ||
679 | } | 558 | } | ||
680 | 559 | | |||
681 | QScriptEngine *ScriptedEffect::engine() const | 560 | QJSEngine *ScriptedEffect::engine() const | ||
682 | { | 561 | { | ||
683 | return m_engine; | 562 | return m_engine; | ||
684 | } | 563 | } | ||
685 | 564 | | |||
686 | } // namespace | 565 | } // namespace | ||
zzag: Unrelated change. | |||||
Please put whitespace between for keyword and '(' and before ':'. Also, please use qAsConst. zzag: Please put whitespace between for keyword and '(' and before ':'. Also, please use qAsConst. |
I suppose we don't need it anymore.