diff --git a/CMakeLists.txt b/CMakeLists.txt index 8556bfa2..4b0ae91b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # SPDX-FileCopyrightText: 2022 Devin Lin # SPDX-License-Identifier: GPL-2.0-or-later -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.24) project(plasma-mobile) set(PROJECT_VERSION "6.3.80") diff --git a/kwin/mobiletaskswitcher/CMakeLists.txt b/kwin/mobiletaskswitcher/CMakeLists.txt index 18d215da..17ad708a 100644 --- a/kwin/mobiletaskswitcher/CMakeLists.txt +++ b/kwin/mobiletaskswitcher/CMakeLists.txt @@ -1,28 +1,10 @@ # SPDX-FileCopyrightText: 2022 Devin Lin # SPDX-License-Identifier: GPL-2.0-or-later -kcoreaddons_add_plugin(mobiletaskswitcher INSTALL_NAMESPACE "kwin/effects/plugins") -target_sources(mobiletaskswitcher PRIVATE - main.cpp - mobiletaskswitchereffect.cpp - effecttouchborder.cpp - taskfiltermodel.cpp - taskmodel.cpp -) +kpackage_install_package(package mobiletaskswitcher effects kwin) -target_link_libraries(mobiletaskswitcher - KF6::ConfigGui - KF6::GlobalAccel - KF6::I18n - KF6::CoreAddons - KF6::WindowSystem +# Copy the script to the build directory so one can run tests without prior +# make install. +file(COPY package/contents package/metadata.json DESTINATION ${CMAKE_BINARY_DIR}/bin/kwin/effects/mobiletaskswitcher) - Qt::Quick - Qt::Core - - KWin::kwin - Plasma::Activities -) - -install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/mobiletaskswitcher) -install(FILES mobiletaskswitcher.json DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/mobiletaskswitcher) +add_subdirectory(plugin) \ No newline at end of file diff --git a/kwin/mobiletaskswitcher/main.cpp b/kwin/mobiletaskswitcher/main.cpp deleted file mode 100644 index a6cdb7c5..00000000 --- a/kwin/mobiletaskswitcher/main.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Devin Lin -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "mobiletaskswitchereffect.h" - -namespace KWin -{ - -KWIN_EFFECT_FACTORY_SUPPORTED(MobileTaskSwitcherEffect, "mobiletaskswitcher.json", return MobileTaskSwitcherEffect::supported();) - -} // namespace KWin - -#include "main.moc" diff --git a/kwin/mobiletaskswitcher/qml/FlickContainer.qml b/kwin/mobiletaskswitcher/package/contents/ui/FlickContainer.qml similarity index 100% rename from kwin/mobiletaskswitcher/qml/FlickContainer.qml rename to kwin/mobiletaskswitcher/package/contents/ui/FlickContainer.qml diff --git a/kwin/mobiletaskswitcher/qml/Task.qml b/kwin/mobiletaskswitcher/package/contents/ui/Task.qml similarity index 100% rename from kwin/mobiletaskswitcher/qml/Task.qml rename to kwin/mobiletaskswitcher/package/contents/ui/Task.qml diff --git a/kwin/mobiletaskswitcher/qml/TaskList.qml b/kwin/mobiletaskswitcher/package/contents/ui/TaskList.qml similarity index 100% rename from kwin/mobiletaskswitcher/qml/TaskList.qml rename to kwin/mobiletaskswitcher/package/contents/ui/TaskList.qml diff --git a/kwin/mobiletaskswitcher/qml/TaskSwitcher.qml b/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml similarity index 98% rename from kwin/mobiletaskswitcher/qml/TaskSwitcher.qml rename to kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml index 2138aab4..4925a054 100644 --- a/kwin/mobiletaskswitcher/qml/TaskSwitcher.qml +++ b/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml @@ -12,7 +12,7 @@ import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell.state as MobileShellState import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings -import org.kde.private.mobileshell.taskswitcher 1.0 as TaskSwitcherData +import org.kde.plasma.private.mobileshell.taskswitcherplugin as TaskSwitcherPlugin import org.kde.kwin 3.0 as KWinComponents import org.kde.kwin.private.effects 1.0 @@ -26,8 +26,8 @@ FocusScope { id: root focus: true + property TaskSwitcherPlugin.MobileTaskSwitcherState state readonly property QtObject effect: KWinComponents.SceneView.effect - readonly property TaskSwitcherData.TaskSwitcherState state: TaskSwitcherData.TaskSwitcherState readonly property QtObject targetScreen: KWinComponents.SceneView.screen readonly property real topMargin: MobileShell.Constants.topPanelHeight @@ -37,7 +37,6 @@ FocusScope { property var taskSwitcherHelpers: TaskSwitcherHelpers { taskSwitcher: root - stateClass: TaskSwitcherData.TaskSwitcherState taskList: taskList } @@ -45,9 +44,9 @@ FocusScope { id: haptics } - property var tasksModel: TaskSwitcherData.TaskFilterModel { + property var tasksModel: TaskSwitcherPlugin.TaskFilterModel { screenName: root.targetScreen.name - windowModel: TaskSwitcherData.TaskModel + windowModel: root.state.taskModel } readonly property int tasksCount: taskList.count @@ -147,11 +146,11 @@ FocusScope { } function instantHide() { - root.effect.deactivate(true); + root.state.deactivate(true); } function hide() { - root.effect.deactivate(false); + root.state.deactivate(false); } Connections { @@ -196,7 +195,7 @@ FocusScope { // setup some values and return to the initial setup so that the user can always navigate with no down time state.wasInActiveTask = taskSwitcherHelpers.openAppAnim.running ? true : false taskList.setTaskOffsetValue(state.wasInActiveTask ? taskSwitcherHelpers.taskOffsetValue : taskSwitcherHelpers.homeOffsetValue, true); - state.status = !state.wasInActiveTask ? (taskSwitcherHelpers.openAppAnim.closeAnim && !taskSwitcherHelpers.taskDrawerWillOpen ? TaskSwitcherData.TaskSwitcherState.Active : TaskSwitcherData.TaskSwitcherState.Inactive) : TaskSwitcherData.TaskSwitcherState.Inactive + state.status = !state.wasInActiveTask ? (taskSwitcherHelpers.openAppAnim.closeAnim && !taskSwitcherHelpers.taskDrawerWillOpen ? TaskSwitcherPlugin.TaskSwitcherState.Active : TaskSwitcherPlugin.TaskSwitcherState.Inactive) : TaskSwitcherPlugin.TaskSwitcherState.Inactive initialSetup(); } else if (taskSwitcherHelpers.openAnim.running) { taskSwitcherHelpers.cancelAnimations(); diff --git a/kwin/mobiletaskswitcher/qml/TaskSwitcherHelpers.qml b/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcherHelpers.qml similarity index 97% rename from kwin/mobiletaskswitcher/qml/TaskSwitcherHelpers.qml rename to kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcherHelpers.qml index 20aa4858..b4975b34 100644 --- a/kwin/mobiletaskswitcher/qml/TaskSwitcherHelpers.qml +++ b/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcherHelpers.qml @@ -5,7 +5,7 @@ import QtQuick 2.15 import org.kde.kirigami 2.20 as Kirigami -import org.kde.private.mobileshell.taskswitcher 1.0 as TaskSwitcherData +import org.kde.plasma.private.mobileshell.taskswitcherplugin as TaskSwitcherPlugin import org.kde.kwin 3.0 as KWinComponents @@ -19,7 +19,6 @@ QtObject { // We assume that the taskSwitcher is the size of the entire screen. required property var taskSwitcher property var state: taskSwitcher.state - required property var stateClass required property var taskList // task switcher peek and pop setting for when it is toggled from the home screen @@ -56,7 +55,7 @@ QtObject { readonly property real heightThreshold: windowHeight * 0.55 // whether the switcher is opened or not - readonly property bool taskDrawerOpened: state.status == TaskSwitcherData.TaskSwitcherState.Active + readonly property bool taskDrawerOpened: state.status == TaskSwitcherPlugin.MobileTaskSwitcherState.Active // This is true when the task drawer is already opened or if within an app readonly property bool notHomeScreenState: state.wasInActiveTask || taskDrawerOpened @@ -312,7 +311,7 @@ QtObject { onFinished: { if (!isInTaskScrubMode) { - root.state.status = stateClass.Active; + root.state.status = TaskSwitcherPlugin.MobileTaskSwitcherState.Active; } } } @@ -328,7 +327,7 @@ QtObject { easing.type: Easing.InBack onFinished: { - root.state.status = stateClass.Inactive; + root.state.status = TaskSwitcherPlugin.MobileTaskSwitcherState.Inactive; taskSwitcher.instantHide(); } } @@ -360,7 +359,7 @@ QtObject { duration: 300 easing.type: Easing.OutQuint onFinished: { - root.state.status = stateClass.Inactive; + root.state.status = TaskSwitcherPlugin.MobileTaskSwitcherState.Inactive; taskSwitcher.instantHide(); } } diff --git a/kwin/mobiletaskswitcher/package/contents/ui/main.qml b/kwin/mobiletaskswitcher/package/contents/ui/main.qml new file mode 100644 index 00000000..98fa1fa7 --- /dev/null +++ b/kwin/mobiletaskswitcher/package/contents/ui/main.qml @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2025 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +import QtQuick + +import org.kde.kwin + +import org.kde.plasma.private.mobileshell.taskswitcherplugin as TaskSwitcherPlugin +import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings + +SceneEffect { + id: root + + // Created per screen + delegate: TaskSwitcher { + id: taskSwitcher + state: taskSwitcherState + } + + ShortcutHandler { + name: 'Mobile Task Switcher' + text: i18n('Toggle Mobile Task Switcher') + sequence: 'Meta+C' + + onActivated: taskSwitcherState.toggle() + } + + TaskSwitcherPlugin.MobileTaskSwitcherState { + id: taskSwitcherState + + gestureEnabled: !ShellSettings.Settings.navigationPanelEnabled + + Component.onCompleted: { + // Initialize with effect + taskSwitcherState.init(root); + } + } +} \ No newline at end of file diff --git a/kwin/mobiletaskswitcher/mobiletaskswitcher.json b/kwin/mobiletaskswitcher/package/metadata.json similarity index 93% rename from kwin/mobiletaskswitcher/mobiletaskswitcher.json rename to kwin/mobiletaskswitcher/package/metadata.json index 7c5b1589..0b83b859 100644 --- a/kwin/mobiletaskswitcher/mobiletaskswitcher.json +++ b/kwin/mobiletaskswitcher/package/metadata.json @@ -1,5 +1,12 @@ { + "KPackageStructure": "KWin/Effect", "KPlugin": { + "Authors": [ + { + "Email": "devin@kde.org", + "Name": "Devin Lin" + } + ], "Category": "Window Management", "Description": "Allows you to switch between running tasks with a mobile interface.", "Description[ca@valencia]": "Us permet canviar entre les tasques diferents en execució amb una interfície mòbil.", @@ -34,8 +41,10 @@ "Description[x-test]": "xxAllows you to switch between running tasks with a mobile interface.xx", "Description[zh_CN]": "允许您使用移动界面在正在运行的任务之间切换。", "Description[zh_TW]": "讓您用手機介面在執行中的工作項目之間切換。", + "Icon": "preferences-system-windows", + "Id": "mobiletaskswitcher", "EnabledByDefault": false, - "License": "GPL", + "License": "GPL-2.0-or-later", "Name": "Mobile Task Switcher", "Name[ca@valencia]": "Commutador de tasques del mòbil", "Name[ca]": "Commutador de tasques del mòbil", @@ -71,5 +80,7 @@ "Name[zh_CN]": "移动任务切换", "Name[zh_TW]": "行動工作切換器" }, - "X-KWin-Border-Activate": true + "X-KWin-Border-Activate": true, + "X-KDE-Ordering": 60, + "X-Plasma-API": "declarativescript" } diff --git a/kwin/mobiletaskswitcher/plugin/CMakeLists.txt b/kwin/mobiletaskswitcher/plugin/CMakeLists.txt new file mode 100644 index 00000000..b4fe4c75 --- /dev/null +++ b/kwin/mobiletaskswitcher/plugin/CMakeLists.txt @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2025 Devin Lin +# SPDX-License-Identifier: GPL-2.0-or-later + +ecm_add_qml_module(mobiletaskswitcherplugin URI org.kde.plasma.private.mobileshell.taskswitcherplugin GENERATE_PLUGIN_SOURCE) + +target_sources(mobiletaskswitcherplugin PRIVATE + mobiletaskswitchereffect.cpp + effecttouchborder.cpp + taskfiltermodel.cpp + taskmodel.cpp) + +target_link_libraries(mobiletaskswitcherplugin PRIVATE + KF6::ConfigGui + KF6::GlobalAccel + KF6::I18n + KF6::CoreAddons + KF6::WindowSystem + + Qt::Quick + Qt::Core + + KWin::kwin + Plasma::Activities +) + +ecm_finalize_qml_module(mobiletaskswitcherplugin) diff --git a/kwin/mobiletaskswitcher/effecttouchborder.cpp b/kwin/mobiletaskswitcher/plugin/effecttouchborder.cpp similarity index 97% rename from kwin/mobiletaskswitcher/effecttouchborder.cpp rename to kwin/mobiletaskswitcher/plugin/effecttouchborder.cpp index 09b5cb2a..f83c3144 100644 --- a/kwin/mobiletaskswitcher/effecttouchborder.cpp +++ b/kwin/mobiletaskswitcher/plugin/effecttouchborder.cpp @@ -57,7 +57,7 @@ void EffectTouchBorder::setBorders(const QList &touchActivateBorders) effects->registerRealtimeTouchBorder(ElectricBorder(border), m_state->activateAction(), [this](ElectricBorder border, const QPointF &deltaProgress, const Output *screen) { - Q_UNUSED(screen) + Q_UNUSED(screen) m_state->setInProgress(true); if (border == ElectricTop || border == ElectricBottom) { diff --git a/kwin/mobiletaskswitcher/effecttouchborder.h b/kwin/mobiletaskswitcher/plugin/effecttouchborder.h similarity index 100% rename from kwin/mobiletaskswitcher/effecttouchborder.h rename to kwin/mobiletaskswitcher/plugin/effecttouchborder.h diff --git a/kwin/mobiletaskswitcher/mobiletaskswitchereffect.cpp b/kwin/mobiletaskswitcher/plugin/mobiletaskswitchereffect.cpp similarity index 63% rename from kwin/mobiletaskswitcher/mobiletaskswitchereffect.cpp rename to kwin/mobiletaskswitcher/plugin/mobiletaskswitchereffect.cpp index 23ca3a6a..5c2f3763 100644 --- a/kwin/mobiletaskswitcher/mobiletaskswitchereffect.cpp +++ b/kwin/mobiletaskswitcher/plugin/mobiletaskswitchereffect.cpp @@ -18,11 +18,56 @@ using namespace std::chrono_literals; namespace KWin { -MobileTaskSwitcherState::MobileTaskSwitcherState(EffectTouchBorderState *effectState) - : m_effectState{effectState} +MobileTaskSwitcherState::MobileTaskSwitcherState(QObject *parent) + : QObject{parent} , m_doubleClickTimer{new QElapsedTimer{}} + , m_shutdownTimer{new QTimer{this}} { + // Configure close timer + m_shutdownTimer->setSingleShot(true); + m_shutdownTimer->setInterval(300ms); + connect(m_shutdownTimer, &QTimer::timeout, this, &MobileTaskSwitcherState::realDeactivate); +} + +void MobileTaskSwitcherState::init(KWin::QuickSceneEffect *parent) +{ + m_effectState = new EffectTouchBorderState(parent); + m_border = new EffectTouchBorder{m_effectState}; + m_taskModel = new TaskModel{parent}; + m_effect = parent; + + // Connect signals + connect(this, &MobileTaskSwitcherState::gestureEnabledChanged, this, &MobileTaskSwitcherState::refreshBorders); + connect(m_border, &EffectTouchBorder::touchPositionChanged, this, &MobileTaskSwitcherState::processTouchPositionChanged); + connect(this, &MobileTaskSwitcherState::gestureInProgressChanged, this, [this]() { + if (gestureInProgress()) { + invokeEffect(); + } + }); connect(m_effectState, &EffectTouchBorderState::inProgressChanged, this, &MobileTaskSwitcherState::gestureInProgressChanged); + connect(effects, &EffectsHandler::screenAboutToLock, this, &MobileTaskSwitcherState::realDeactivate); + + refreshBorders(); +} + +bool MobileTaskSwitcherState::gestureEnabled() const +{ + return m_gestureEnabled; +} + +void MobileTaskSwitcherState::setGestureEnabled(bool gestureEnabled) +{ + m_gestureEnabled = gestureEnabled; + Q_EMIT gestureEnabledChanged(); +} + +void MobileTaskSwitcherState::refreshBorders() +{ + if (m_gestureEnabled) { + m_border->setBorders({ElectricBorder::ElectricBottom}); + } else { + m_border->setBorders({}); + } } bool MobileTaskSwitcherState::gestureInProgress() const @@ -124,6 +169,11 @@ void MobileTaskSwitcherState::setYPosition(qreal yPosition) } } +MobileTaskSwitcherState::Status MobileTaskSwitcherState::status() const +{ + return m_status; +} + void MobileTaskSwitcherState::setStatus(Status status) { if (m_status != status) { @@ -135,6 +185,11 @@ void MobileTaskSwitcherState::setStatus(Status status) } } +int MobileTaskSwitcherState::currentTaskIndex() const +{ + return m_currentTaskIndex; +} + void MobileTaskSwitcherState::setCurrentTaskIndex(int newTaskIndex) { if (m_currentTaskIndex != newTaskIndex) { @@ -143,6 +198,11 @@ void MobileTaskSwitcherState::setCurrentTaskIndex(int newTaskIndex) } } +int MobileTaskSwitcherState::initialTaskIndex() const +{ + return m_initialTaskIndex; +} + void MobileTaskSwitcherState::setInitialTaskIndex(int newTaskIndex) { if (m_initialTaskIndex != newTaskIndex) { @@ -151,6 +211,11 @@ void MobileTaskSwitcherState::setInitialTaskIndex(int newTaskIndex) } } +TaskModel *MobileTaskSwitcherState::taskModel() const +{ + return m_taskModel; +} + void MobileTaskSwitcherState::restartDoubleClickTimer() { m_doubleClickTimer->restart(); @@ -198,100 +263,27 @@ void MobileTaskSwitcherState::processTouchPositionChanged(qreal primaryDelta, qr qint64 MobileTaskSwitcherState::getElapsedTimeSinceStart() { - if (m_doubleClickTimer->isValid()) - { + if (m_doubleClickTimer->isValid()) { return m_doubleClickTimer->elapsed(); } return -1; } -MobileTaskSwitcherEffect::MobileTaskSwitcherEffect() - : m_effectState{new EffectTouchBorderState(this)} - , m_taskSwitcherState{new MobileTaskSwitcherState(m_effectState)} - , m_taskModel{new TaskModel{this}} - , m_border{new EffectTouchBorder{m_effectState}} - , m_toggleAction{std::make_unique()} - , m_shutdownTimer{new QTimer{this}} +void MobileTaskSwitcherState::toggle() { - const char *uri = "org.kde.private.mobileshell.taskswitcher"; - qmlRegisterType(uri, 1, 0, "TaskFilterModel"); - qmlRegisterSingletonType(uri, 1, 0, "TaskModel", [this](QQmlEngine *, QJSEngine *) -> QObject * { - return m_taskModel; - }); - qmlRegisterSingletonType(uri, 1, 0, "TaskSwitcherState", [this](QQmlEngine *, QJSEngine *) -> QObject * { - return m_taskSwitcherState; - }); - - connect(m_border, &EffectTouchBorder::touchPositionChanged, m_taskSwitcherState, &MobileTaskSwitcherState::processTouchPositionChanged); - - connect(m_taskSwitcherState, &MobileTaskSwitcherState::gestureInProgressChanged, this, [this]() { - if (m_taskSwitcherState->gestureInProgress()) { - invokeEffect(); - } - }); - - // configure close timer - m_shutdownTimer->setSingleShot(true); - connect(m_shutdownTimer, &QTimer::timeout, this, &MobileTaskSwitcherEffect::realDeactivate); - - // toggle action - const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_C; - - m_toggleAction.get()->setObjectName(QStringLiteral("Mobile Task Switcher")); - m_toggleAction.get()->setText(i18n("Toggle Mobile Task Switcher")); - KGlobalAccel::self()->setDefaultShortcut(m_toggleAction.get(), {defaultToggleShortcut}); - KGlobalAccel::self()->setShortcut(m_toggleAction.get(), {defaultToggleShortcut}); - connect(m_toggleAction.get(), &QAction::triggered, this, &MobileTaskSwitcherEffect::toggle); - - connect(effects, &EffectsHandler::screenAboutToLock, this, &MobileTaskSwitcherEffect::realDeactivate); - - setSource(QUrl::fromLocalFile( - QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/mobiletaskswitcher/qml/TaskSwitcher.qml")))); - reconfigure(ReconfigureFlag::ReconfigureAll); -} - -MobileTaskSwitcherEffect::~MobileTaskSwitcherEffect() -{ -} - -void MobileTaskSwitcherEffect::reconfigure(ReconfigureFlags) -{ - setAnimationDuration(animationTime(300ms)); - m_border->setBorders(m_borderActivate); -} - -int MobileTaskSwitcherEffect::requestedEffectChainPosition() const -{ - return 70; -} - -bool MobileTaskSwitcherEffect::borderActivated(ElectricBorder border) -{ - return m_borderActivate.contains(border); -} - -void MobileTaskSwitcherEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent) -{ - if (m_toggleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { - if (keyEvent->type() == QEvent::KeyPress) { - toggle(); - } + if (!m_effect) { return; } - QuickSceneEffect::grabbedKeyboardEvent(keyEvent); -} -void MobileTaskSwitcherEffect::toggle() -{ - if (!isRunning()) { - m_taskSwitcherState->restartDoubleClickTimer(); + if (!m_effect->isRunning()) { + restartDoubleClickTimer(); activate(); } else { deactivate(false); } } -void MobileTaskSwitcherEffect::activate() +void MobileTaskSwitcherState::activate() { if (effects->isScreenLocked()) { return; @@ -301,44 +293,39 @@ void MobileTaskSwitcherEffect::activate() invokeEffect(); } -void MobileTaskSwitcherEffect::deactivate(bool deactivateInstantly) +void MobileTaskSwitcherState::deactivate(bool deactivateInstantly) { + if (!m_effect) { + return; + } + const auto screens = effects->screens(); for (const auto screen : screens) { - if (QuickSceneView *view = viewForScreen(screen)) { + if (QuickSceneView *view = m_effect->viewForScreen(screen)) { QMetaObject::invokeMethod(view->rootItem(), "hideAnimation"); } } - m_shutdownTimer->start(animationTime(deactivateInstantly ? 0ms : 200ms)); + m_shutdownTimer->start(m_effect->animationTime(deactivateInstantly ? 0ms : 200ms)); } -void MobileTaskSwitcherEffect::realDeactivate() +void MobileTaskSwitcherState::realDeactivate() { + if (!m_effect || !m_effectState) { + return; + } + m_effectState->setInProgress(false); - m_taskSwitcherState->setStatus(MobileTaskSwitcherState::Status::Inactive); - setRunning(false); + setStatus(MobileTaskSwitcherState::Status::Inactive); + m_effect->setRunning(false); setDBusState(false); } -void MobileTaskSwitcherEffect::quickDeactivate() +void MobileTaskSwitcherState::quickDeactivate() { m_shutdownTimer->start(0); } -int MobileTaskSwitcherEffect::animationDuration() const -{ - return m_animationDuration; -} - -void MobileTaskSwitcherEffect::setAnimationDuration(int duration) -{ - if (m_animationDuration != duration) { - m_animationDuration = duration; - Q_EMIT animationDurationChanged(); - } -} - -void MobileTaskSwitcherEffect::setDBusState(bool active) +void MobileTaskSwitcherState::setDBusState(bool active) { QDBusMessage request = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), QStringLiteral("/Mobile"), @@ -350,11 +337,10 @@ void MobileTaskSwitcherEffect::setDBusState(bool active) QDBusConnection::sessionBus().send(request); } -void MobileTaskSwitcherEffect::invokeEffect() +void MobileTaskSwitcherState::invokeEffect() { - m_taskSwitcherState->setInitialTaskIndex( - m_taskSwitcherState->currentTaskIndex()); // TODO! this is only until the crashing bug is fixed and recency sorting is in - setRunning(true); + setInitialTaskIndex(currentTaskIndex()); // TODO! this is only until the crashing bug is fixed and recency sorting is in + m_effect->setRunning(true); setDBusState(true); } } diff --git a/kwin/mobiletaskswitcher/mobiletaskswitchereffect.h b/kwin/mobiletaskswitcher/plugin/mobiletaskswitchereffect.h similarity index 80% rename from kwin/mobiletaskswitcher/mobiletaskswitchereffect.h rename to kwin/mobiletaskswitcher/plugin/mobiletaskswitchereffect.h index ce93aa2e..c8d25b25 100644 --- a/kwin/mobiletaskswitcher/mobiletaskswitchereffect.h +++ b/kwin/mobiletaskswitcher/plugin/mobiletaskswitchereffect.h @@ -30,6 +30,8 @@ namespace KWin class MobileTaskSwitcherState : public QObject { Q_OBJECT + Q_PROPERTY(bool gestureEnabled READ gestureEnabled WRITE setGestureEnabled NOTIFY gestureEnabledChanged) + Q_PROPERTY(bool wasInActiveTask READ wasInActiveTask WRITE setWasInActiveTask NOTIFY wasInActiveTaskChanged) Q_PROPERTY(int currentTaskIndex READ currentTaskIndex WRITE setCurrentTaskIndex NOTIFY currentTaskIndexChanged) Q_PROPERTY(int initialTaskIndex READ initialTaskIndex WRITE setInitialTaskIndex NOTIFY initialTaskIndexChanged) @@ -51,6 +53,9 @@ class MobileTaskSwitcherState : public QObject Q_PROPERTY(qint64 elapsedTimeSinceStart READ getElapsedTimeSinceStart) Q_PROPERTY(qint64 doubleClickInterval READ getDoubleClickInterval) // is there a better way than to forward this? + Q_PROPERTY(TaskModel *taskModel READ taskModel CONSTANT) + QML_ELEMENT + public: enum class Status { // TODO! I could (should?) re-add the activating and deactivating states again to match EffectTogglableState. could help with/tie into @@ -61,7 +66,12 @@ public: }; Q_ENUM(Status) - MobileTaskSwitcherState(EffectTouchBorderState *effectState); + MobileTaskSwitcherState(QObject *parent = nullptr); + + Q_INVOKABLE void init(KWin::QuickSceneEffect *parent); + + bool gestureEnabled() const; + void setGestureEnabled(bool gestureEnabled); bool gestureInProgress() const; void setGestureInProgress(bool gestureInProgress); @@ -82,30 +92,34 @@ public: qreal yPosition() const; void setYPosition(qreal positionY); + Status status() const; void setStatus(Status status); - Status status() const - { - return m_status; - } + int currentTaskIndex() const; void setCurrentTaskIndex(int newTaskIndex); - int currentTaskIndex() const - { - return m_currentTaskIndex; - } + int initialTaskIndex() const; void setInitialTaskIndex(int newTaskIndex); - int initialTaskIndex() const - { - return m_initialTaskIndex; - } void restartDoubleClickTimer(); + int animationDuration() const; + void setDBusState(bool active); + + TaskModel *taskModel() const; + public Q_SLOTS: void processTouchPositionChanged(qreal primaryPosition, qreal orthogonalPosition); + void activate(); + void realDeactivate(); + void deactivate(bool deactivateInstantly); + void quickDeactivate(); + void toggle(); + Q_SIGNALS: + void gestureEnabledChanged(); + void activated(); void deactivated(); @@ -125,9 +139,19 @@ Q_SIGNALS: void xPositionChanged(); void yPositionChanged(); +private Q_SLOTS: + void refreshBorders(); + private: + void invokeEffect(); + + bool m_gestureEnabled{false}; + EffectTouchBorderState *m_effectState{nullptr}; + EffectTouchBorder *m_border{nullptr}; + TaskModel *m_taskModel{nullptr}; + KWin::QuickSceneEffect *m_effect{nullptr}; + Status m_status = Status::Inactive; - EffectTouchBorderState *m_effectState; bool m_gestureInProgress = false; int m_currentTaskIndex; @@ -161,53 +185,8 @@ private: { return qApp->doubleClickInterval(); } -}; - -class MobileTaskSwitcherEffect : public QuickSceneEffect -{ - Q_OBJECT - -public: - enum class Status { Inactive, Activating, Deactivating, Active }; - MobileTaskSwitcherEffect(); - ~MobileTaskSwitcherEffect() override; - - int animationDuration() const; - void setAnimationDuration(int duration); - - int requestedEffectChainPosition() const override; - bool borderActivated(ElectricBorder border) override; - void reconfigure(ReconfigureFlags flags) override; - void grabbedKeyboardEvent(QKeyEvent *keyEvent) override; - - void setDBusState(bool active); - -public Q_SLOTS: - void activate(); - void realDeactivate(); - void deactivate(bool deactivateInstantly); - void quickDeactivate(); - void toggle(); - -Q_SIGNALS: - void animationDurationChanged(); - void gestureInProgressChanged(); - -private: - void invokeEffect(); - - EffectTouchBorderState *const m_effectState; - MobileTaskSwitcherState *const m_taskSwitcherState; - TaskModel *const m_taskModel; - EffectTouchBorder *const m_border; - QList m_borderActivate = {ElectricBorder::ElectricBottom}; - - std::unique_ptr m_toggleAction; - QList m_toggleShortcut; QTimer *m_shutdownTimer; - - int m_animationDuration = 400; }; } // namespace KWin diff --git a/kwin/mobiletaskswitcher/taskfiltermodel.cpp b/kwin/mobiletaskswitcher/plugin/taskfiltermodel.cpp similarity index 100% rename from kwin/mobiletaskswitcher/taskfiltermodel.cpp rename to kwin/mobiletaskswitcher/plugin/taskfiltermodel.cpp diff --git a/kwin/mobiletaskswitcher/taskfiltermodel.h b/kwin/mobiletaskswitcher/plugin/taskfiltermodel.h similarity index 96% rename from kwin/mobiletaskswitcher/taskfiltermodel.h rename to kwin/mobiletaskswitcher/plugin/taskfiltermodel.h index 10336e9d..7e34bb1b 100644 --- a/kwin/mobiletaskswitcher/taskfiltermodel.h +++ b/kwin/mobiletaskswitcher/plugin/taskfiltermodel.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -21,6 +22,7 @@ class TaskFilterModel : public QSortFilterProxyModel Q_OBJECT Q_PROPERTY(TaskModel *windowModel READ windowModel WRITE setWindowModel NOTIFY windowModelChanged) Q_PROPERTY(QString screenName READ screenName WRITE setScreenName NOTIFY screenNameChanged) + QML_ELEMENT public: explicit TaskFilterModel(QObject *parent = nullptr); diff --git a/kwin/mobiletaskswitcher/taskmodel.cpp b/kwin/mobiletaskswitcher/plugin/taskmodel.cpp similarity index 100% rename from kwin/mobiletaskswitcher/taskmodel.cpp rename to kwin/mobiletaskswitcher/plugin/taskmodel.cpp diff --git a/kwin/mobiletaskswitcher/taskmodel.h b/kwin/mobiletaskswitcher/plugin/taskmodel.h similarity index 87% rename from kwin/mobiletaskswitcher/taskmodel.h rename to kwin/mobiletaskswitcher/plugin/taskmodel.h index a92238c1..5e302b40 100644 --- a/kwin/mobiletaskswitcher/taskmodel.h +++ b/kwin/mobiletaskswitcher/plugin/taskmodel.h @@ -19,7 +19,13 @@ class TaskModel : public QAbstractListModel Q_OBJECT public: - enum Roles { WindowRole = Qt::UserRole + 1, OutputRole, DesktopRole, ActivityRole, LastActivatedRole }; + enum Roles { + WindowRole = Qt::UserRole + 1, + OutputRole, + DesktopRole, + ActivityRole, + LastActivatedRole + }; explicit TaskModel(QObject *parent = nullptr);