2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
#include "kwin_wayland_test.h"
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2023-11-17 08:59:08 +00:00
|
|
|
#include "effect/anidata_p.h"
|
2023-11-20 14:53:25 +00:00
|
|
|
#include "effect/effecthandler.h"
|
2023-11-17 08:59:08 +00:00
|
|
|
#include "effect/effectloader.h"
|
2022-03-23 10:13:38 +00:00
|
|
|
#include "scripting/scriptedeffect.h"
|
2018-07-23 21:52:58 +00:00
|
|
|
#include "virtualdesktops.h"
|
|
|
|
#include "wayland_server.h"
|
2022-04-22 17:39:12 +00:00
|
|
|
#include "window.h"
|
2018-07-23 21:52:58 +00:00
|
|
|
#include "workspace.h"
|
|
|
|
|
|
|
|
#include <KConfigGroup>
|
|
|
|
#include <KGlobalAccel>
|
|
|
|
#include <KWayland/Client/compositor.h>
|
|
|
|
#include <KWayland/Client/connection_thread.h>
|
|
|
|
#include <KWayland/Client/registry.h>
|
|
|
|
#include <KWayland/Client/slide.h>
|
|
|
|
#include <KWayland/Client/surface.h>
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
#include <QJSValue>
|
|
|
|
#include <QQmlEngine>
|
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
using namespace KWin;
|
2018-10-24 16:39:37 +00:00
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
static const QString s_socketName = QStringLiteral("wayland_test_effects_scripts-0");
|
|
|
|
|
|
|
|
class ScriptedEffectsTest : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
private Q_SLOTS:
|
|
|
|
void initTestCase();
|
|
|
|
void init();
|
|
|
|
void cleanup();
|
|
|
|
|
|
|
|
void testEffectsHandler();
|
|
|
|
void testEffectsContext();
|
|
|
|
void testShortcuts();
|
|
|
|
void testAnimations_data();
|
|
|
|
void testAnimations();
|
|
|
|
void testScreenEdge();
|
|
|
|
void testScreenEdgeTouch();
|
2018-10-03 00:11:59 +00:00
|
|
|
void testFullScreenEffect_data();
|
|
|
|
void testFullScreenEffect();
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
void testKeepAlive_data();
|
|
|
|
void testKeepAlive();
|
2018-10-27 20:13:53 +00:00
|
|
|
void testGrab();
|
|
|
|
void testGrabAlreadyGrabbedWindow();
|
|
|
|
void testGrabAlreadyGrabbedWindowForced();
|
|
|
|
void testUngrab();
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
void testRedirect_data();
|
|
|
|
void testRedirect();
|
2018-10-25 18:02:36 +00:00
|
|
|
void testComplete();
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
private:
|
|
|
|
ScriptedEffect *loadEffect(const QString &name);
|
|
|
|
};
|
|
|
|
|
|
|
|
class ScriptedEffectWithDebugSpy : public KWin::ScriptedEffect
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
ScriptedEffectWithDebugSpy();
|
|
|
|
bool load(const QString &name);
|
2019-02-26 09:37:44 +00:00
|
|
|
using AnimationEffect::AniMap;
|
2018-07-23 21:52:58 +00:00
|
|
|
using AnimationEffect::state;
|
2022-03-23 10:13:38 +00:00
|
|
|
Q_INVOKABLE void sendTestResponse(const QString &out); // proxies triggers out from the tests
|
|
|
|
QList<QAction *> actions(); // returns any QActions owned by the ScriptEngine
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_SIGNALS:
|
2018-07-23 21:52:58 +00:00
|
|
|
void testOutput(const QString &data);
|
|
|
|
};
|
|
|
|
|
2021-02-01 21:08:56 +00:00
|
|
|
void ScriptedEffectWithDebugSpy::sendTestResponse(const QString &out)
|
2018-07-23 21:52:58 +00:00
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT testOutput(out);
|
2021-02-01 21:08:56 +00:00
|
|
|
}
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2021-02-01 21:08:56 +00:00
|
|
|
QList<QAction *> ScriptedEffectWithDebugSpy::actions()
|
|
|
|
{
|
|
|
|
return findChildren<QAction *>(QString(), Qt::FindDirectChildrenOnly);
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptedEffectWithDebugSpy::ScriptedEffectWithDebugSpy()
|
|
|
|
: ScriptedEffect()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScriptedEffectWithDebugSpy::load(const QString &name)
|
|
|
|
{
|
2021-02-01 21:08:56 +00:00
|
|
|
auto selfContext = engine()->newQObject(this);
|
|
|
|
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
|
2018-07-23 21:52:58 +00:00
|
|
|
const QString path = QFINDTESTDATA("./scripts/" + name + ".js");
|
2021-02-01 21:08:56 +00:00
|
|
|
engine()->globalObject().setProperty("sendTestResponse", selfContext.property("sendTestResponse"));
|
2022-03-23 10:13:38 +00:00
|
|
|
if (!init(name, path)) {
|
2018-07-23 21:52:58 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-11-15 09:51:56 +00:00
|
|
|
// inject our newly created effect to be registered with the EffectsHandler::loaded_effects
|
2018-07-23 21:52:58 +00:00
|
|
|
// this is private API so some horrible code is used to find the internal effectloader
|
|
|
|
// and register ourselves
|
2022-04-23 19:51:16 +00:00
|
|
|
auto children = effects->children();
|
|
|
|
for (auto it = children.begin(); it != children.end(); ++it) {
|
2018-07-23 21:52:58 +00:00
|
|
|
if (qstrcmp((*it)->metaObject()->className(), "KWin::EffectLoader") != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-03-23 10:13:38 +00:00
|
|
|
QMetaObject::invokeMethod(*it, "effectLoaded", Q_ARG(KWin::Effect *, this), Q_ARG(QString, name));
|
2018-07-23 21:52:58 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-11-15 09:51:56 +00:00
|
|
|
return effects->isEffectLoaded(name);
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::initTestCase()
|
|
|
|
{
|
2023-04-15 19:28:12 +00:00
|
|
|
if (!Test::renderNodeAvailable()) {
|
|
|
|
QSKIP("no render node available");
|
|
|
|
return;
|
|
|
|
}
|
2022-04-22 17:39:12 +00:00
|
|
|
qRegisterMetaType<KWin::Window *>();
|
2022-03-23 10:13:38 +00:00
|
|
|
qRegisterMetaType<KWin::Effect *>();
|
2020-07-07 09:32:29 +00:00
|
|
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
2020-12-09 13:06:15 +00:00
|
|
|
QVERIFY(waylandServer()->init(s_socketName));
|
2023-05-08 10:16:00 +00:00
|
|
|
Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
|
2018-07-23 21:52:58 +00:00
|
|
|
|
|
|
|
// disable all effects - we don't want to have it interact with the rendering
|
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
KConfigGroup plugins(config, QStringLiteral("Plugins"));
|
2021-10-09 15:38:14 +00:00
|
|
|
const auto builtinNames = EffectLoader().listOfKnownEffects();
|
2018-07-23 21:52:58 +00:00
|
|
|
for (QString name : builtinNames) {
|
|
|
|
plugins.writeEntry(name + QStringLiteral("Enabled"), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
config->sync();
|
|
|
|
kwinApp()->setConfig(config);
|
|
|
|
|
|
|
|
qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2"));
|
|
|
|
qputenv("KWIN_EFFECTS_FORCE_ANIMATIONS", "1");
|
|
|
|
kwinApp()->start();
|
2020-07-07 09:32:29 +00:00
|
|
|
QVERIFY(applicationStartedSpy.wait());
|
2019-01-06 15:34:10 +00:00
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
KWin::VirtualDesktopManager::self()->setCount(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::init()
|
|
|
|
{
|
|
|
|
QVERIFY(Test::setupWaylandConnection());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::cleanup()
|
|
|
|
{
|
|
|
|
Test::destroyWaylandConnection();
|
2019-01-19 14:26:34 +00:00
|
|
|
|
2023-11-15 09:51:56 +00:00
|
|
|
effects->unloadAllEffects();
|
|
|
|
QVERIFY(effects->loadedEffects().isEmpty());
|
2019-01-19 14:26:34 +00:00
|
|
|
|
2018-10-03 00:11:59 +00:00
|
|
|
KWin::VirtualDesktopManager::self()->setCurrent(1);
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testEffectsHandler()
|
|
|
|
{
|
|
|
|
// this triggers and tests some of the signals in EffectHandler, which is exposed to JS as context property "effects"
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy; // cleaned up in ::clean
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
2020-08-26 17:24:02 +00:00
|
|
|
auto waitFor = [&effectOutputSpy](const QString &expected) {
|
2018-08-20 16:39:36 +00:00
|
|
|
QVERIFY(effectOutputSpy.count() > 0 || effectOutputSpy.wait());
|
|
|
|
QCOMPARE(effectOutputSpy.first().first(), expected);
|
|
|
|
effectOutputSpy.removeFirst();
|
2018-07-23 21:52:58 +00:00
|
|
|
};
|
|
|
|
QVERIFY(effect->load("effectsHandler"));
|
|
|
|
|
|
|
|
// trigger windowAdded signal
|
|
|
|
|
|
|
|
// create a window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-07-23 21:52:58 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-07-23 21:52:58 +00:00
|
|
|
QVERIFY(shellSurface);
|
2021-05-11 05:26:51 +00:00
|
|
|
shellSurface->set_title("WindowA");
|
2022-08-16 11:43:33 +00:00
|
|
|
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2018-07-23 21:52:58 +00:00
|
|
|
QVERIFY(c);
|
2022-04-23 08:33:23 +00:00
|
|
|
QCOMPARE(workspace()->activeWindow(), c);
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2018-08-20 16:39:36 +00:00
|
|
|
waitFor("windowAdded - WindowA");
|
|
|
|
waitFor("stackingOrder - 1 WindowA");
|
2018-07-23 21:52:58 +00:00
|
|
|
|
|
|
|
// windowMinimsed
|
2023-03-07 10:45:02 +00:00
|
|
|
c->setMinimized(true);
|
2018-08-20 16:39:36 +00:00
|
|
|
waitFor("windowMinimized - WindowA");
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2023-03-07 10:45:02 +00:00
|
|
|
c->setMinimized(false);
|
2018-08-20 16:39:36 +00:00
|
|
|
waitFor("windowUnminimized - WindowA");
|
2018-07-23 21:52:58 +00:00
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
surface.reset();
|
2018-08-20 16:39:36 +00:00
|
|
|
waitFor("windowClosed - WindowA");
|
2018-07-23 21:52:58 +00:00
|
|
|
|
|
|
|
// desktop management
|
|
|
|
KWin::VirtualDesktopManager::self()->setCurrent(2);
|
|
|
|
waitFor("desktopChanged - 1 2");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testEffectsContext()
|
|
|
|
{
|
|
|
|
// this tests misc non-objects exposed to the script engine: animationTime, displaySize, use of external enums
|
|
|
|
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy; // cleaned up in ::clean
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load("effectContext"));
|
|
|
|
QCOMPARE(effectOutputSpy[0].first(), "1280x1024");
|
|
|
|
QCOMPARE(effectOutputSpy[1].first(), "100");
|
|
|
|
QCOMPARE(effectOutputSpy[2].first(), "2");
|
|
|
|
QCOMPARE(effectOutputSpy[3].first(), "0");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testShortcuts()
|
|
|
|
{
|
2023-11-20 22:28:23 +00:00
|
|
|
#if !KWIN_BUILD_GLOBALSHORTCUTS
|
|
|
|
QSKIP("Can't test shortcuts without shortcuts");
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
// this tests method registerShortcut
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy; // cleaned up in ::clean
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load("shortcutsTest"));
|
2021-02-01 21:08:56 +00:00
|
|
|
QCOMPARE(effect->actions().count(), 1);
|
|
|
|
auto action = effect->actions()[0];
|
2018-07-23 21:52:58 +00:00
|
|
|
QCOMPARE(action->objectName(), "testShortcut");
|
|
|
|
QCOMPARE(action->text(), "Test Shortcut");
|
|
|
|
QCOMPARE(KGlobalAccel::self()->shortcut(action).first(), QKeySequence("Meta+Shift+Y"));
|
|
|
|
action->trigger();
|
|
|
|
QCOMPARE(effectOutputSpy[0].first(), "shortcutTriggered");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testAnimations_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
QTest::addColumn<int>("animationCount");
|
|
|
|
|
|
|
|
QTest::newRow("single") << "animationTest" << 1;
|
2022-03-23 10:13:38 +00:00
|
|
|
QTest::newRow("multi") << "animationTestMulti" << 2;
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testAnimations()
|
|
|
|
{
|
|
|
|
// this tests animate/set/cancel
|
|
|
|
// methods take either an int or an array, as forced in the data above
|
|
|
|
// also splits animate vs effects.animate(..)
|
|
|
|
|
|
|
|
QFETCH(QString, file);
|
|
|
|
QFETCH(int, animationCount);
|
|
|
|
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load(file));
|
|
|
|
|
|
|
|
// animated after window added connect
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-07-23 21:52:58 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-07-23 21:52:58 +00:00
|
|
|
QVERIFY(shellSurface);
|
2021-05-11 05:26:51 +00:00
|
|
|
shellSurface->set_title("Window 1");
|
2022-08-16 11:43:33 +00:00
|
|
|
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2018-07-23 21:52:58 +00:00
|
|
|
QVERIFY(c);
|
2022-04-23 08:33:23 +00:00
|
|
|
QCOMPARE(workspace()->activeWindow(), c);
|
2018-07-23 21:52:58 +00:00
|
|
|
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(c->effectWindow()));
|
|
|
|
const auto &animationsForWindow = state.at(c->effectWindow()).first;
|
|
|
|
QCOMPARE(animationsForWindow.size(), animationCount);
|
2018-10-24 16:39:37 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].timeLine.duration(), 100ms);
|
2018-07-23 21:52:58 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].to, FPx2(1.4));
|
|
|
|
QCOMPARE(animationsForWindow[0].attribute, AnimationEffect::Scale);
|
2020-03-04 10:51:25 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].timeLine.easingCurve().type(), QEasingCurve::OutCubic);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].terminationFlags,
|
|
|
|
AnimationEffect::TerminateAtSource | AnimationEffect::TerminateAtTarget);
|
2018-10-24 16:39:37 +00:00
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
if (animationCount == 2) {
|
2018-10-24 16:39:37 +00:00
|
|
|
QCOMPARE(animationsForWindow[1].timeLine.duration(), 100ms);
|
2018-07-23 21:52:58 +00:00
|
|
|
QCOMPARE(animationsForWindow[1].to, FPx2(0.0));
|
|
|
|
QCOMPARE(animationsForWindow[1].attribute, AnimationEffect::Opacity);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animationsForWindow[1].terminationFlags,
|
|
|
|
AnimationEffect::TerminateAtSource | AnimationEffect::TerminateAtTarget);
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
QCOMPARE(effectOutputSpy[0].first(), "true");
|
|
|
|
|
|
|
|
// window state changes, scale should be retargetted
|
|
|
|
|
|
|
|
c->setMinimized(true);
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
const auto &animationsForWindow = state.at(c->effectWindow()).first;
|
|
|
|
QCOMPARE(animationsForWindow.size(), animationCount);
|
2018-10-24 16:39:37 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].timeLine.duration(), 200ms);
|
2018-07-23 21:52:58 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].to, FPx2(1.5));
|
|
|
|
QCOMPARE(animationsForWindow[0].attribute, AnimationEffect::Scale);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animationsForWindow[0].terminationFlags,
|
|
|
|
AnimationEffect::TerminateAtSource | AnimationEffect::TerminateAtTarget);
|
2018-07-23 21:52:58 +00:00
|
|
|
if (animationCount == 2) {
|
2018-10-24 16:39:37 +00:00
|
|
|
QCOMPARE(animationsForWindow[1].timeLine.duration(), 200ms);
|
2018-07-23 21:52:58 +00:00
|
|
|
QCOMPARE(animationsForWindow[1].to, FPx2(1.5));
|
|
|
|
QCOMPARE(animationsForWindow[1].attribute, AnimationEffect::Opacity);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animationsForWindow[1].terminationFlags,
|
|
|
|
AnimationEffect::TerminateAtSource | AnimationEffect::TerminateAtTarget);
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
c->setMinimized(false);
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 0);
|
2018-07-23 21:52:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testScreenEdge()
|
|
|
|
{
|
|
|
|
// this test checks registerScreenEdge functions
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy; // cleaned up in ::clean
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load("screenEdgeTest"));
|
|
|
|
effect->borderActivated(KWin::ElectricTopRight);
|
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testScreenEdgeTouch()
|
|
|
|
{
|
|
|
|
// this test checks registerTouchScreenEdge functions
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy; // cleaned up in ::clean
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load("screenEdgeTouchTest"));
|
2021-02-01 21:08:56 +00:00
|
|
|
effect->actions()[0]->trigger();
|
2018-07-23 21:52:58 +00:00
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
}
|
|
|
|
|
2018-10-03 00:11:59 +00:00
|
|
|
void ScriptedEffectsTest::testFullScreenEffect_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
|
|
|
|
QTest::newRow("single") << "fullScreenEffectTest";
|
2022-03-23 10:13:38 +00:00
|
|
|
QTest::newRow("multi") << "fullScreenEffectTestMulti";
|
2018-10-10 08:58:17 +00:00
|
|
|
QTest::newRow("global") << "fullScreenEffectTestGlobal";
|
2018-10-03 00:11:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testFullScreenEffect()
|
|
|
|
{
|
|
|
|
QFETCH(QString, file);
|
|
|
|
|
|
|
|
auto *effectMain = new ScriptedEffectWithDebugSpy; // cleaned up in ::clean
|
|
|
|
QSignalSpy effectOutputSpy(effectMain, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QSignalSpy fullScreenEffectActiveSpy(effects, &EffectsHandler::hasActiveFullScreenEffectChanged);
|
|
|
|
QSignalSpy isActiveFullScreenEffectSpy(effectMain, &ScriptedEffect::isActiveFullScreenEffectChanged);
|
|
|
|
|
|
|
|
QVERIFY(effectMain->load(file));
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// load any random effect from another test to confirm fullscreen effect state is correctly
|
|
|
|
// shown as being someone else
|
2018-10-03 00:11:59 +00:00
|
|
|
auto effectOther = new ScriptedEffectWithDebugSpy();
|
|
|
|
QVERIFY(effectOther->load("screenEdgeTouchTest"));
|
|
|
|
QSignalSpy isActiveFullScreenEffectSpyOther(effectOther, &ScriptedEffect::isActiveFullScreenEffectChanged);
|
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-10-03 00:11:59 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-10-03 00:11:59 +00:00
|
|
|
QVERIFY(shellSurface);
|
2021-05-11 05:26:51 +00:00
|
|
|
shellSurface->set_title("Window 1");
|
2022-08-16 11:43:33 +00:00
|
|
|
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2018-10-03 00:11:59 +00:00
|
|
|
QVERIFY(c);
|
2022-04-23 08:33:23 +00:00
|
|
|
QCOMPARE(workspace()->activeWindow(), c);
|
2018-10-03 00:11:59 +00:00
|
|
|
|
|
|
|
QCOMPARE(effects->hasActiveFullScreenEffect(), false);
|
|
|
|
QCOMPARE(effectMain->isActiveFullScreenEffect(), false);
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// trigger animation
|
2018-10-03 00:11:59 +00:00
|
|
|
KWin::VirtualDesktopManager::self()->setCurrent(2);
|
|
|
|
|
|
|
|
QCOMPARE(effects->activeFullScreenEffect(), effectMain);
|
|
|
|
QCOMPARE(effects->hasActiveFullScreenEffect(), true);
|
|
|
|
QCOMPARE(fullScreenEffectActiveSpy.count(), 1);
|
|
|
|
|
|
|
|
QCOMPARE(effectMain->isActiveFullScreenEffect(), true);
|
|
|
|
QCOMPARE(isActiveFullScreenEffectSpy.count(), 1);
|
|
|
|
|
|
|
|
QCOMPARE(effectOther->isActiveFullScreenEffect(), false);
|
|
|
|
QCOMPARE(isActiveFullScreenEffectSpyOther.count(), 0);
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// after 500ms trigger another full screen animation
|
2018-10-03 00:11:59 +00:00
|
|
|
QTest::qWait(500);
|
|
|
|
KWin::VirtualDesktopManager::self()->setCurrent(1);
|
|
|
|
QCOMPARE(effects->activeFullScreenEffect(), effectMain);
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// after 1000ms (+a safety margin for time based tests) we should still be the active full screen effect
|
|
|
|
// despite first animation expiring
|
|
|
|
QTest::qWait(500 + 100);
|
2018-10-03 00:11:59 +00:00
|
|
|
QCOMPARE(effects->activeFullScreenEffect(), effectMain);
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// after 1500ms (+a safetey margin) we should have no full screen effect
|
|
|
|
QTest::qWait(500 + 100);
|
2018-10-03 00:11:59 +00:00
|
|
|
QCOMPARE(effects->activeFullScreenEffect(), nullptr);
|
|
|
|
}
|
|
|
|
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
void ScriptedEffectsTest::testKeepAlive_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
QTest::addColumn<bool>("keepAlive");
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
QTest::newRow("keep") << "keepAliveTest" << true;
|
|
|
|
QTest::newRow("don't keep") << "keepAliveTestDontKeep" << false;
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testKeepAlive()
|
|
|
|
{
|
|
|
|
// this test checks whether closed windows are kept alive
|
|
|
|
// when keepAlive property is set to true(false)
|
|
|
|
|
|
|
|
QFETCH(QString, file);
|
|
|
|
QFETCH(bool, keepAlive);
|
|
|
|
|
|
|
|
auto *effect = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load(file));
|
|
|
|
|
|
|
|
// create a window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
QVERIFY(c);
|
2022-04-23 08:33:23 +00:00
|
|
|
QCOMPARE(workspace()->activeWindow(), c);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
|
|
|
|
// no active animations at the beginning
|
2024-08-06 22:02:09 +00:00
|
|
|
QCOMPARE(effect->state().size(), 0);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
|
|
|
|
// trigger windowClosed signal
|
2023-03-02 18:32:12 +00:00
|
|
|
QSignalSpy deletedRemovedSpy(workspace(), &Workspace::deletedRemoved);
|
2022-08-16 11:43:33 +00:00
|
|
|
surface.reset();
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
QVERIFY(effectOutputSpy.count() == 1 || effectOutputSpy.wait());
|
|
|
|
|
|
|
|
if (keepAlive) {
|
2024-08-06 22:02:09 +00:00
|
|
|
QCOMPARE(effect->state().size(), 1);
|
2023-03-02 18:32:12 +00:00
|
|
|
QCOMPARE(deletedRemovedSpy.count(), 0);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
|
|
|
|
QTest::qWait(500);
|
2024-08-06 22:02:09 +00:00
|
|
|
QCOMPARE(effect->state().size(), 1);
|
2023-03-02 18:32:12 +00:00
|
|
|
QCOMPARE(deletedRemovedSpy.count(), 0);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
|
|
|
|
QTest::qWait(500 + 100); // 100ms is extra safety margin
|
2023-03-02 18:32:12 +00:00
|
|
|
QCOMPARE(deletedRemovedSpy.count(), 1);
|
2024-08-06 22:02:09 +00:00
|
|
|
QCOMPARE(effect->state().size(), 0);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
} else {
|
|
|
|
// the test effect doesn't keep the window alive, so it should be
|
|
|
|
// removed immediately
|
|
|
|
QVERIFY(deletedRemovedSpy.count() == 1 || deletedRemovedSpy.wait(100)); // 100ms is less than duration of the animation
|
2024-08-06 22:02:09 +00:00
|
|
|
QCOMPARE(effect->state().size(), 0);
|
[effects/dialogparent] Fix flickering of parent windows
Summary:
If a modal window is closed and some alternative effect that animates
the disappearing of windows is enabled(e.g. the Glide effect, or the
Scale effect), the Dialog Parent effect can cause flickering of the
parent window because its animation duration doesn't match duration of
those alternative effects.
Also, if the Fade effect, the Glide effect, and the Scale effect are
disabled, the Dialog Parent will keep the parent window alive for no
good reason.
This change addresses that problem by adding keepAlive property to
`animate` function so scripted effects have more control over lifetime
of animated windows.
If both a modal window and its parent window are closed at the same time
(and there is no effect that animates the disappearing of windows), the
Dialog Parent will stop immediately(because windowDeleted will be
emitted right after windowClosed signal).
If both a modal window and its parent window are closed at the same time
(and there is effect that animates the disappearing of windows), the
Dialog Parent won't reference the latter window. Thus, it won't cause
flickering. I.e. it will "passively" animate parent windows.
BUG: 355036
FIXED-IN: 5.15.0
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D14919
2018-10-10 07:36:45 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-03 00:11:59 +00:00
|
|
|
|
2018-10-27 20:13:53 +00:00
|
|
|
void ScriptedEffectsTest::testGrab()
|
|
|
|
{
|
|
|
|
// this test verifies that scripted effects can grab windows that are
|
|
|
|
// not already grabbed
|
|
|
|
|
|
|
|
// load the test effect
|
|
|
|
auto effect = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load(QStringLiteral("grabTest")));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// create test window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(workspace()->activeWindow(), window);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// the test effect should grab the test window successfully
|
2018-10-27 20:13:53 +00:00
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(effectOutputSpy.first().first(), QStringLiteral("ok"));
|
2022-04-23 19:51:16 +00:00
|
|
|
QCOMPARE(window->effectWindow()->data(WindowAddedGrabRole).value<void *>(), effect);
|
2018-10-27 20:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testGrabAlreadyGrabbedWindow()
|
|
|
|
{
|
|
|
|
// this test verifies that scripted effects cannot grab already grabbed
|
|
|
|
// windows (unless force is set to true of course)
|
|
|
|
|
|
|
|
// load effect that will hold the window grab
|
|
|
|
auto owner = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy ownerOutputSpy(owner, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(owner->load(QStringLiteral("grabAlreadyGrabbedWindowTest_owner")));
|
|
|
|
|
|
|
|
// load effect that will try to grab already grabbed window
|
|
|
|
auto grabber = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy grabberOutputSpy(grabber, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(grabber->load(QStringLiteral("grabAlreadyGrabbedWindowTest_grabber")));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// create test window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(workspace()->activeWindow(), window);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
|
|
|
// effect that initially held the grab should still hold the grab
|
|
|
|
QCOMPARE(ownerOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(ownerOutputSpy.first().first(), QStringLiteral("ok"));
|
2022-04-23 19:51:16 +00:00
|
|
|
QCOMPARE(window->effectWindow()->data(WindowAddedGrabRole).value<void *>(), owner);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
|
|
|
// effect that tried to grab already grabbed window should fail miserably
|
|
|
|
QCOMPARE(grabberOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(grabberOutputSpy.first().first(), QStringLiteral("fail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testGrabAlreadyGrabbedWindowForced()
|
|
|
|
{
|
|
|
|
// this test verifies that scripted effects can steal window grabs when
|
|
|
|
// they forcefully try to grab windows
|
|
|
|
|
|
|
|
// load effect that initially will be holding the window grab
|
|
|
|
auto owner = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy ownerOutputSpy(owner, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(owner->load(QStringLiteral("grabAlreadyGrabbedWindowForcedTest_owner")));
|
|
|
|
|
|
|
|
// load effect that will try to steal the window grab
|
|
|
|
auto thief = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy thiefOutputSpy(thief, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(thief->load(QStringLiteral("grabAlreadyGrabbedWindowForcedTest_thief")));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// create test window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(workspace()->activeWindow(), window);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
|
|
|
// verify that the owner in fact held the grab
|
|
|
|
QCOMPARE(ownerOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(ownerOutputSpy.first().first(), QStringLiteral("ok"));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// effect that grabbed the test window forcefully should now hold the grab
|
2018-10-27 20:13:53 +00:00
|
|
|
QCOMPARE(thiefOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(thiefOutputSpy.first().first(), QStringLiteral("ok"));
|
2022-04-23 19:51:16 +00:00
|
|
|
QCOMPARE(window->effectWindow()->data(WindowAddedGrabRole).value<void *>(), thief);
|
2018-10-27 20:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testUngrab()
|
|
|
|
{
|
|
|
|
// this test verifies that scripted effects can ungrab windows that they
|
|
|
|
// are previously grabbed
|
|
|
|
|
|
|
|
// load the test effect
|
|
|
|
auto effect = new ScriptedEffectWithDebugSpy;
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
QVERIFY(effect->load(QStringLiteral("ungrabTest")));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// create test window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-10-27 20:13:53 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(workspace()->activeWindow(), window);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// the test effect should grab the test window successfully
|
2018-10-27 20:13:53 +00:00
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(effectOutputSpy.first().first(), QStringLiteral("ok"));
|
2022-04-23 19:51:16 +00:00
|
|
|
QCOMPARE(window->effectWindow()->data(WindowAddedGrabRole).value<void *>(), effect);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
|
|
|
// when the test effect sees that a window was minimized, it will try to ungrab it
|
|
|
|
effectOutputSpy.clear();
|
2022-04-23 19:51:16 +00:00
|
|
|
window->setMinimized(true);
|
2018-10-27 20:13:53 +00:00
|
|
|
|
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(effectOutputSpy.first().first(), QStringLiteral("ok"));
|
2022-04-23 19:51:16 +00:00
|
|
|
QCOMPARE(window->effectWindow()->data(WindowAddedGrabRole).value<void *>(), nullptr);
|
2018-10-27 20:13:53 +00:00
|
|
|
}
|
|
|
|
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
void ScriptedEffectsTest::testRedirect_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
QTest::addColumn<bool>("shouldTerminate");
|
|
|
|
QTest::newRow("animate/DontTerminateAtSource") << "redirectAnimateDontTerminateTest" << false;
|
2022-03-23 10:13:38 +00:00
|
|
|
QTest::newRow("animate/TerminateAtSource") << "redirectAnimateTerminateTest" << true;
|
|
|
|
QTest::newRow("set/DontTerminate") << "redirectSetDontTerminateTest" << false;
|
|
|
|
QTest::newRow("set/Terminate") << "redirectSetTerminateTest" << true;
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptedEffectsTest::testRedirect()
|
|
|
|
{
|
|
|
|
// this test verifies that redirect() works
|
|
|
|
|
|
|
|
// load the test effect
|
|
|
|
auto effect = new ScriptedEffectWithDebugSpy;
|
|
|
|
QFETCH(QString, file);
|
|
|
|
QVERIFY(effect->load(file));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// create test window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(workspace()->activeWindow(), window);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
auto around = [](std::chrono::milliseconds elapsed,
|
|
|
|
std::chrono::milliseconds pivot,
|
|
|
|
std::chrono::milliseconds margin) {
|
2022-11-21 17:23:00 +00:00
|
|
|
return std::abs(elapsed.count() - pivot.count()) < margin.count();
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// initially, the test animation is at the source position
|
|
|
|
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(window->effectWindow()));
|
|
|
|
const auto &animations = state.at(window->effectWindow()).first;
|
|
|
|
QCOMPARE(animations.size(), 1);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animations[0].timeLine.direction(), TimeLine::Forward);
|
|
|
|
QVERIFY(around(animations[0].timeLine.elapsed(), 0ms, 50ms));
|
|
|
|
}
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// minimize the test window after 250ms, when the test effect sees that
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
// a window was minimized, it will try to reverse animation for it
|
|
|
|
QTest::qWait(250);
|
|
|
|
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
window->setMinimized(true);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
|
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(effectOutputSpy.first().first(), QStringLiteral("ok"));
|
|
|
|
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(window->effectWindow()));
|
|
|
|
const auto &animations = state.at(window->effectWindow()).first;
|
|
|
|
QCOMPARE(animations.size(), 1);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animations[0].timeLine.direction(), TimeLine::Backward);
|
|
|
|
QVERIFY(around(animations[0].timeLine.elapsed(), 1000ms - 250ms, 50ms));
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait for the animation to reach the start position, 100ms is an extra
|
|
|
|
// safety margin
|
|
|
|
QTest::qWait(250 + 100);
|
|
|
|
|
|
|
|
QFETCH(bool, shouldTerminate);
|
|
|
|
if (shouldTerminate) {
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 0);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
} else {
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(window->effectWindow()));
|
|
|
|
const auto &animations = state.at(window->effectWindow()).first;
|
|
|
|
QCOMPARE(animations.size(), 1);
|
[scripting] Introduce redirect function
Summary:
Consider current implementation of the Squash effect: if a window was
minimized, an animation will be started; if the window is unminimized
and the animation is still active (that can happen when user clicks on
app's icon really fast), the animation will be stopped and a new one will
be created. Such behavior can lead to rapid jumps in the observed
"animation".
A better approach would be first try to **reverse** the already active
animation, and if that attempt wasn't successful, start a new animation.
This patch introduces a new function to the scripted effects API that
lets JavaScript effects to control direction of animations. The
prototype of the function looks as follows:
redirect(<animation id(s)>, <direction>, [<termination policy>])
the first argument is an animation id or a list of animation ids, the
second argument specifies the new direction of the animation or
animations if a list of ids was passed as the first argument. The
third argument specifies whether the animation(s) should be terminated
when it(they) reaches the source position, currently it's relevant only
for animations that are created with set() function. The termination
policy argument is optional, by default it's Effect.TerminateAtSource.
We can use this function to fix issues with rapid jumps in the Squash
effect. Also, redirect() lets us to write effects for simple animations
in slightly different style: first, we have to start the main animation
(e.g. for the Dialog Parent effect, it would be dimming of main windows)
and then change direction of the animation depending on external events,
e.g. when the Desktop Cube effect is activated.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, abetts, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D16449
2018-10-24 19:58:29 +00:00
|
|
|
QCOMPARE(animations[0].timeLine.direction(), TimeLine::Backward);
|
|
|
|
QCOMPARE(animations[0].timeLine.elapsed(), 1000ms);
|
|
|
|
QCOMPARE(animations[0].timeLine.value(), 0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 18:02:36 +00:00
|
|
|
void ScriptedEffectsTest::testComplete()
|
|
|
|
{
|
|
|
|
// this test verifies that complete works
|
|
|
|
|
|
|
|
// load the test effect
|
|
|
|
auto effect = new ScriptedEffectWithDebugSpy;
|
|
|
|
QVERIFY(effect->load(QStringLiteral("completeTest")));
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// create test window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
2018-10-25 18:02:36 +00:00
|
|
|
QVERIFY(surface);
|
2024-03-10 14:32:54 +00:00
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
2018-10-25 18:02:36 +00:00
|
|
|
QVERIFY(shellSurface);
|
2022-08-16 11:43:33 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(workspace()->activeWindow(), window);
|
2018-10-25 18:02:36 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
auto around = [](std::chrono::milliseconds elapsed,
|
|
|
|
std::chrono::milliseconds pivot,
|
|
|
|
std::chrono::milliseconds margin) {
|
2022-11-21 17:23:00 +00:00
|
|
|
return std::abs(elapsed.count() - pivot.count()) < margin.count();
|
2018-10-25 18:02:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// initially, the test animation should be at the start position
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(window->effectWindow()));
|
|
|
|
const auto &animations = state.at(window->effectWindow()).first;
|
|
|
|
QCOMPARE(animations.size(), 1);
|
2018-10-25 18:02:36 +00:00
|
|
|
QVERIFY(around(animations[0].timeLine.elapsed(), 0ms, 50ms));
|
|
|
|
QVERIFY(!animations[0].timeLine.done());
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait for 250ms
|
|
|
|
QTest::qWait(250);
|
|
|
|
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(window->effectWindow()));
|
|
|
|
const auto &animations = state.at(window->effectWindow()).first;
|
|
|
|
QCOMPARE(animations.size(), 1);
|
2018-10-25 18:02:36 +00:00
|
|
|
QVERIFY(around(animations[0].timeLine.elapsed(), 250ms, 50ms));
|
|
|
|
QVERIFY(!animations[0].timeLine.done());
|
|
|
|
}
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
// minimize the test window, when the test effect sees that a window was
|
2018-10-25 18:02:36 +00:00
|
|
|
// minimized, it will try to complete animation for it
|
|
|
|
QSignalSpy effectOutputSpy(effect, &ScriptedEffectWithDebugSpy::testOutput);
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
window->setMinimized(true);
|
2018-10-25 18:02:36 +00:00
|
|
|
|
|
|
|
QCOMPARE(effectOutputSpy.count(), 1);
|
|
|
|
QCOMPARE(effectOutputSpy.first().first(), QStringLiteral("ok"));
|
|
|
|
|
|
|
|
{
|
2024-08-06 22:02:09 +00:00
|
|
|
const auto &state = effect->state();
|
|
|
|
QCOMPARE(state.size(), 1);
|
|
|
|
QVERIFY(state.contains(window->effectWindow()));
|
|
|
|
const auto &animations = state.at(window->effectWindow()).first;
|
|
|
|
QCOMPARE(animations.size(), 1);
|
2018-10-25 18:02:36 +00:00
|
|
|
QCOMPARE(animations[0].timeLine.elapsed(), 1000ms);
|
|
|
|
QVERIFY(animations[0].timeLine.done());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-23 21:52:58 +00:00
|
|
|
WAYLANDTEST_MAIN(ScriptedEffectsTest)
|
|
|
|
#include "scripted_effects_test.moc"
|