From a0f456b7dcc5dfea836bd0d878f0d5e4f0f03312 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 22 Nov 2021 16:44:30 +0200 Subject: [PATCH] effects: Add Window View effect The Window View effect is a companion effect for the task manager. It provides the task manager a way to ask the user to pick a window among windows identified by window ids passed to the activate() method. The main motivation for adding this effect is to provide a QtQuick based alternative for the present windows effect, which is needed to kill the latter. This change doesn't extend the overview effect to avoid repeating mistakes of the past, i.e. adding too many (unrelated) features to the present windows effect. While the overview effect provides you a way to select windows, it's not the only thing that it has. For example, it also allows changing virtual desktops, etc. On the other hand, the task manager doesn't need all of that. It only needs to ask the user to pick a window among the specified windows, nothing more. As is, the window view effect is simply WindowHeap with a d-bus api slapped on top of it. --- src/effects/CMakeLists.txt | 2 + src/effects/windowview/CMakeLists.txt | 14 +++ src/effects/windowview/main.cpp | 18 +++ src/effects/windowview/metadata.json | 13 ++ .../org.kde.KWin.Effect.WindowView1.xml | 24 ++++ src/effects/windowview/qml/main.qml | 113 ++++++++++++++++++ src/effects/windowview/windowvieweffect.cpp | 92 ++++++++++++++ src/effects/windowview/windowvieweffect.h | 38 ++++++ 8 files changed, 314 insertions(+) create mode 100644 src/effects/windowview/CMakeLists.txt create mode 100644 src/effects/windowview/main.cpp create mode 100644 src/effects/windowview/metadata.json create mode 100644 src/effects/windowview/org.kde.KWin.Effect.WindowView1.xml create mode 100644 src/effects/windowview/qml/main.qml create mode 100644 src/effects/windowview/windowvieweffect.cpp create mode 100644 src/effects/windowview/windowvieweffect.h diff --git a/src/effects/CMakeLists.txt b/src/effects/CMakeLists.txt index 325371cc17..504a1838c8 100644 --- a/src/effects/CMakeLists.txt +++ b/src/effects/CMakeLists.txt @@ -86,6 +86,8 @@ add_subdirectory(slideback) add_subdirectory(slidingpopups) add_subdirectory(thumbnailaside) add_subdirectory(touchpoints) +add_subdirectory(windowgeometry) +add_subdirectory(windowview) add_subdirectory(zoom) # OpenGL-specific effects diff --git a/src/effects/windowview/CMakeLists.txt b/src/effects/windowview/CMakeLists.txt new file mode 100644 index 0000000000..d32b448c02 --- /dev/null +++ b/src/effects/windowview/CMakeLists.txt @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2021 Vlad Zahorodnii +# +# SPDX-License-Identifier: BSD-3-Clause + +set(windowview_SOURCES + main.cpp + windowvieweffect.cpp +) + +qt_add_dbus_adaptor(windowview_SOURCES org.kde.KWin.Effect.WindowView1.xml windowvieweffect.h KWin::WindowViewEffect) + +kwin4_add_effect_module(kwin4_effect_windowview ${windowview_SOURCES}) + +install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/windowview) diff --git a/src/effects/windowview/main.cpp b/src/effects/windowview/main.cpp new file mode 100644 index 0000000000..103acac383 --- /dev/null +++ b/src/effects/windowview/main.cpp @@ -0,0 +1,18 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "windowvieweffect.h" + +namespace KWin +{ + +KWIN_EFFECT_FACTORY_SUPPORTED(WindowViewEffect, + "metadata.json.stripped", + return WindowViewEffect::supported();) + +} // namespace KWin + +#include "main.moc" diff --git a/src/effects/windowview/metadata.json b/src/effects/windowview/metadata.json new file mode 100644 index 0000000000..1ae50baa81 --- /dev/null +++ b/src/effects/windowview/metadata.json @@ -0,0 +1,13 @@ +{ + "KPlugin": { + "Category": "Window Management", + "Description": "Helper effect for the task manager", + "EnabledByDefault": true, + "Id": "windowview", + "License": "GPL", + "Name": "Window View" + }, + "org.kde.kwin.effect": { + "internal": true + } +} diff --git a/src/effects/windowview/org.kde.KWin.Effect.WindowView1.xml b/src/effects/windowview/org.kde.KWin.Effect.WindowView1.xml new file mode 100644 index 0000000000..8cd6fb1a57 --- /dev/null +++ b/src/effects/windowview/org.kde.KWin.Effect.WindowView1.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/src/effects/windowview/qml/main.qml b/src/effects/windowview/qml/main.qml new file mode 100644 index 0000000000..e9988d34d8 --- /dev/null +++ b/src/effects/windowview/qml/main.qml @@ -0,0 +1,113 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +import QtQuick 2.12 +import QtGraphicalEffects 1.12 +import org.kde.kwin 3.0 as KWinComponents +import org.kde.kwin.private.effects 1.0 +import org.kde.plasma.core 2.0 as PlasmaCore + +Item { + id: container + + required property QtObject effect + required property QtObject targetScreen + required property var selectedIds + + property bool animationEnabled: false + property bool organized: false + + readonly property int animationDuration: PlasmaCore.Units.longDuration + + function start() { + container.animationEnabled = true; + container.organized = true; + } + + function stop() { + container.organized = false; + } + + Keys.onEscapePressed: effect.deactivate(animationDuration); + + KWinComponents.DesktopBackgroundItem { + activity: KWinComponents.Workspace.currentActivity + desktop: KWinComponents.Workspace.currentVirtualDesktop + outputName: targetScreen.name + + layer.enabled: true + layer.effect: FastBlur { + radius: container.organized ? 64 : 0 + Behavior on radius { + NumberAnimation { duration: effect.animationDuration; easing.type: Easing.OutCubic } + } + } + } + + Rectangle { + anchors.fill: parent + color: PlasmaCore.ColorScope.backgroundColor + opacity: container.organized ? 0.75 : 0 + + TapHandler { + onTapped: effect.deactivate(animationDuration); + } + + Behavior on opacity { + OpacityAnimator { duration: animationDuration; easing.type: Easing.OutCubic } + } + } + + WindowHeap { + width: targetScreen.geometry.width + height: targetScreen.geometry.height + focus: true + padding: PlasmaCore.Units.largeSpacing + animationDuration: container.animationDuration + animationEnabled: container.animationEnabled + dragEnabled: false + organized: container.organized + showOnly: selectedIds + model: KWinComponents.ClientFilterModel { + activity: KWinComponents.Workspace.currentActivity + desktop: KWinComponents.Workspace.currentVirtualDesktop + screenName: targetScreen.name + clientModel: stackModel + windowType: ~KWinComponents.ClientFilterModel.Dock & + ~KWinComponents.ClientFilterModel.Desktop & + ~KWinComponents.ClientFilterModel.Notification; + } + onActivated: effect.deactivate(animationDuration); + } + + Repeater { + model: KWinComponents.ClientFilterModel { + desktop: KWinComponents.Workspace.currentVirtualDesktop + screenName: targetScreen.name + clientModel: stackModel + windowType: KWinComponents.ClientFilterModel.Dock + } + + KWinComponents.WindowThumbnailItem { + id: windowThumbnail + wId: model.client.internalId + x: model.client.x - targetScreen.geometry.x + y: model.client.y - targetScreen.geometry.y + visible: opacity > 0 + opacity: (model.client.hidden || container.organized) ? 0 : 1 + + Behavior on opacity { + NumberAnimation { duration: animationDuration; easing.type: Easing.OutCubic } + } + } + } + + KWinComponents.ClientModel { + id: stackModel + } + + Component.onCompleted: start(); +} diff --git a/src/effects/windowview/windowvieweffect.cpp b/src/effects/windowview/windowvieweffect.cpp new file mode 100644 index 0000000000..4f060b3d51 --- /dev/null +++ b/src/effects/windowview/windowvieweffect.cpp @@ -0,0 +1,92 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "windowvieweffect.h" +#include "windowview1adaptor.h" + +#include +#include + +namespace KWin +{ + +static const QString s_dbusServiceName = QStringLiteral("org.kde.KWin.Effect.WindowView1"); +static const QString s_dbusObjectPath = QStringLiteral("/org/kde/KWin/Effect/WindowView1"); + +WindowViewEffect::WindowViewEffect() + : m_shutdownTimer(new QTimer(this)) +{ + new WindowView1Adaptor(this); + + QDBusConnection::sessionBus().registerObject(s_dbusObjectPath, this); + QDBusConnection::sessionBus().registerService(s_dbusServiceName); + + m_shutdownTimer->setSingleShot(true); + connect(m_shutdownTimer, &QTimer::timeout, this, &WindowViewEffect::realDeactivate); + connect(effects, &EffectsHandler::screenAboutToLock, this, &WindowViewEffect::realDeactivate); + + setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/windowview/qml/main.qml")))); +} + +WindowViewEffect::~WindowViewEffect() +{ + QDBusConnection::sessionBus().unregisterService(s_dbusServiceName); + QDBusConnection::sessionBus().unregisterObject(s_dbusObjectPath); +} + +QVariantMap WindowViewEffect::initialProperties(EffectScreen *screen) +{ + return QVariantMap { + { QStringLiteral("effect"), QVariant::fromValue(this) }, + { QStringLiteral("targetScreen"), QVariant::fromValue(screen) }, + { QStringLiteral("selectedIds"), QVariant::fromValue(m_windowIds) }, + }; +} + +int WindowViewEffect::requestedEffectChainPosition() const +{ + return 70; +} + +void WindowViewEffect::activate(const QStringList &windowIds) +{ + QList internalIds; + internalIds.reserve(windowIds.count()); + for (const QString &windowId : windowIds) { + if (const auto window = effects->findWindow(windowId)) { + internalIds.append(window->internalId()); + continue; + } + + // On X11, the task manager can pass a list with X11 ids. + bool ok; + if (const long legacyId = windowId.toLong(&ok); ok) { + if (const auto window = effects->findWindow(legacyId)) { + internalIds.append(window->internalId()); + } + } + } + if (!internalIds.isEmpty()) { + m_windowIds = internalIds; + setRunning(true); + } +} + +void WindowViewEffect::deactivate(int timeout) +{ + const auto screenViews = views(); + for (QuickSceneView *view : screenViews) { + QMetaObject::invokeMethod(view->rootItem(), "stop"); + } + m_shutdownTimer->start(timeout); +} + +void WindowViewEffect::realDeactivate() +{ + setRunning(false); +} + +} // namespace KWin diff --git a/src/effects/windowview/windowvieweffect.h b/src/effects/windowview/windowvieweffect.h new file mode 100644 index 0000000000..3918415b76 --- /dev/null +++ b/src/effects/windowview/windowvieweffect.h @@ -0,0 +1,38 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +namespace KWin +{ + +class WindowViewEffect : public QuickSceneEffect +{ + Q_OBJECT + +public: + WindowViewEffect(); + ~WindowViewEffect() override; + + int requestedEffectChainPosition() const override; + +public Q_SLOTS: + void activate(const QStringList &windowIds); + void deactivate(int timeout); + +protected: + QVariantMap initialProperties(EffectScreen *screen) override; + +private: + void realDeactivate(); + + QTimer *m_shutdownTimer; + QList m_windowIds; +}; + +} // namespace KWin