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