diff --git a/CMakeLists.txt b/CMakeLists.txt index 6341db1a29..e952c123f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON) find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Concurrent Core + Core5Compat DBus Quick UiTools @@ -455,6 +456,7 @@ if (KF6DocTools_FOUND) endif() add_subdirectory(data) +add_subdirectory(kconf_update) add_subdirectory(src) if (BUILD_TESTING) diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt new file mode 100644 index 0000000000..93018f6efa --- /dev/null +++ b/kconf_update/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2023 Niccolò Venerandi +# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +install(FILES kwin.upd + DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) +install(PROGRAMS kwin-6.0-overview-activities-shortcuts.py + DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) diff --git a/kconf_update/kwin-6.0-overview-activities-shortcuts.py b/kconf_update/kwin-6.0-overview-activities-shortcuts.py new file mode 100644 index 0000000000..e50049034d --- /dev/null +++ b/kconf_update/kwin-6.0-overview-activities-shortcuts.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2023 Niccolò Venerandi +# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import fileinput + +for line in fileinput.input(): + if line.startswith('next activity'): + print(line.replace('Meta+Tab', 'Meta+A')) + elif line.startswith('previous activity'): + print(line.replace('Meta+Shift+Tab', 'Meta+Shift+A')) + elif line.startswith('ShowDesktopGrid'): + pass + elif line.startswith('Overview'): + print('Overview=Meta+W,Meta+W,Toggle Overview') + print('Cycle Overview=Meta+Tab,Meta+Tab,Cycle through Overview and Grid View') + print('Cycle Overview Opposite=Meta+Shift+Tab,Meta+Shift+Tab,Cycle through Grid View and Overview') + print('Grid View=Meta+G,Meta+G,Toggle Grid View') + else: + print(line) diff --git a/kconf_update/kwin.upd b/kconf_update/kwin.upd new file mode 100644 index 0000000000..96cce984d2 --- /dev/null +++ b/kconf_update/kwin.upd @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2023 Niccolò Venerandi +# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +Version=5 + +# Changes the default Activities shortcut from Meta+Tab to Meta+A, +# so that the Overview can take its place +Id=change-activities-overview-shortcuts +File=kglobalshortcutsrc +Group=plasmashell,kwin +Script=kwin-6.0-overview-activities-shortcuts.py,python3 diff --git a/src/libkwineffects/effecttogglablestate.cpp b/src/libkwineffects/effecttogglablestate.cpp index 109e9a5cbd..721a80bf35 100644 --- a/src/libkwineffects/effecttogglablestate.cpp +++ b/src/libkwineffects/effecttogglablestate.cpp @@ -43,9 +43,9 @@ EffectTogglableState::EffectTogglableState(Effect *effect) void EffectTogglableState::activate() { - setStatus(Status::Active); setInProgress(false); - setPartialActivationFactor(0.0); + setPartialActivationFactor(1.0); + setStatus(Status::Active); } void EffectTogglableState::setPartialActivationFactor(qreal factor) @@ -60,6 +60,13 @@ void EffectTogglableState::deactivate() { setInProgress(false); setPartialActivationFactor(0.0); + setStatus(Status::Inactive); +} + +void EffectTogglableState::stop() +{ + setInProgress(false); + setStatus(Status::Stopped); } bool EffectTogglableState::inProgress() const @@ -103,7 +110,7 @@ void EffectTogglableState::partialDeactivate(qreal factor) void EffectTogglableState::toggle() { - if (m_status == Status::Inactive || m_partialActivationFactor > 0.5) { + if (m_status == Status::Inactive) { activate(); Q_EMIT activated(); } else { @@ -114,6 +121,9 @@ void EffectTogglableState::toggle() void EffectTogglableState::setProgress(qreal progress) { + if (m_status == Status::Stopped) { + return; + } if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == parent()) { switch (m_status) { case Status::Inactive: @@ -128,6 +138,9 @@ void EffectTogglableState::setProgress(qreal progress) void EffectTogglableState::setRegress(qreal regress) { + if (m_status == Status::Stopped) { + return; + } if (!effects->hasActiveFullScreenEffect() || effects->activeFullScreenEffect() == parent()) { switch (m_status) { case Status::Active: diff --git a/src/libkwineffects/effecttogglablestate.h b/src/libkwineffects/effecttogglablestate.h index 966e85777e..225eabf1d6 100644 --- a/src/libkwineffects/effecttogglablestate.h +++ b/src/libkwineffects/effecttogglablestate.h @@ -29,7 +29,8 @@ public: Inactive, Activating, Deactivating, - Active + Active, + Stopped }; Q_ENUM(Status) @@ -61,6 +62,7 @@ public: void activate(); void deactivate(); void toggle(); + void stop(); void setStatus(Status status); Status status() const { diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 91dd1559c0..f88585e932 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -54,7 +54,6 @@ add_subdirectory(buttonrebinds) add_subdirectory(colord-integration) add_subdirectory(colorpicker) add_subdirectory(desktopchangeosd) -add_subdirectory(desktopgrid) add_subdirectory(dialogparent) add_subdirectory(diminactive) add_subdirectory(dimscreen) diff --git a/src/plugins/desktopgrid/CMakeLists.txt b/src/plugins/desktopgrid/CMakeLists.txt deleted file mode 100644 index 31b9d90d05..0000000000 --- a/src/plugins/desktopgrid/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Vlad Zahorodnii -# -# SPDX-License-Identifier: BSD-3-Clause - -set(desktopgrid_SOURCES - main.cpp - desktopgrideffect.cpp -) - -kconfig_add_kcfg_files(desktopgrid_SOURCES - desktopgridconfig.kcfgc -) - - -kwin_add_builtin_effect(desktopgrid ${desktopgrid_SOURCES}) - -target_link_libraries(desktopgrid PRIVATE - kwineffects - - KF6::ConfigGui - KF6::GlobalAccel - KF6::I18n - - Qt::Quick - ) - -####################################### -# Config -if (KWIN_BUILD_KCMS) - set(kwin_desktopgrid_config_SRCS desktopgrid_config.cpp) - ki18n_wrap_ui(kwin_desktopgrid_config_SRCS desktopgrid_config.ui) - kconfig_add_kcfg_files(kwin_desktopgrid_config_SRCS desktopgridconfig.kcfgc) - - kwin_add_effect_config(kwin_desktopgrid_config ${kwin_desktopgrid_config_SRCS}) - - target_link_libraries(kwin_desktopgrid_config - KF6::KCMUtils - KF6::CoreAddons - KF6::GlobalAccel - KF6::I18n - KF6::XmlGui - Qt::Quick - kwineffects - KWinEffectsInterface - ) -endif() - - -install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/desktopgrid) diff --git a/src/plugins/desktopgrid/desktopgrid_config.cpp b/src/plugins/desktopgrid/desktopgrid_config.cpp deleted file mode 100644 index 7399c065a1..0000000000 --- a/src/plugins/desktopgrid/desktopgrid_config.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2007 Rivo Laks - SPDX-FileCopyrightText: 2008 Lucas Murray - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#include "desktopgrid_config.h" - -#include - -// KConfigSkeleton -#include "desktopgridconfig.h" -#include - -#include - -#include -#include -#include -#include -#include - -#include - -K_PLUGIN_CLASS(KWin::DesktopGridEffectConfig) - -namespace KWin -{ - -DesktopGridEffectConfigForm::DesktopGridEffectConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - -DesktopGridEffectConfig::DesktopGridEffectConfig(QObject *parent, const KPluginMetaData &data) - : KCModule(parent, data) - , m_ui(widget()) -{ - QVBoxLayout *layout = new QVBoxLayout(widget()); - layout->addWidget(&m_ui); - - // Shortcut config. The shortcut belongs to the component "kwin"! - m_actionCollection = new KActionCollection(widget(), QStringLiteral("kwin")); - - m_actionCollection->setComponentDisplayName(i18n("KWin")); - m_actionCollection->setConfigGroup(QStringLiteral("DesktopGrid")); - m_actionCollection->setConfigGlobal(true); - - QAction *a = m_actionCollection->addAction(QStringLiteral("ShowDesktopGrid")); - a->setText(i18n("Show Desktop Grid")); - a->setProperty("isConfigurationAction", true); - KGlobalAccel::self()->setDefaultShortcut(a, QList() << (Qt::CTRL | Qt::Key_F8)); - KGlobalAccel::self()->setShortcut(a, QList() << (Qt::CTRL | Qt::Key_F8)); - - m_ui.shortcutEditor->addCollection(m_actionCollection); - - m_ui.desktopNameAlignmentCombo->addItem(i18nc("Desktop name alignment:", "Disabled"), QVariant(Qt::Alignment())); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Top"), QVariant(Qt::AlignHCenter | Qt::AlignTop)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Top-Right"), QVariant(Qt::AlignRight | Qt::AlignTop)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Right"), QVariant(Qt::AlignRight | Qt::AlignVCenter)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Bottom-Right"), QVariant(Qt::AlignRight | Qt::AlignBottom)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Bottom"), QVariant(Qt::AlignHCenter | Qt::AlignBottom)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Bottom-Left"), QVariant(Qt::AlignLeft | Qt::AlignBottom)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Left"), QVariant(Qt::AlignLeft | Qt::AlignVCenter)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Top-Left"), QVariant(Qt::AlignLeft | Qt::AlignTop)); - m_ui.desktopNameAlignmentCombo->addItem(i18n("Center"), QVariant(Qt::AlignCenter)); - - DesktopGridConfig::instance(KWIN_CONFIG); - addConfig(DesktopGridConfig::self(), &m_ui); - connect(m_ui.kcfg_DesktopLayoutMode, qOverload(&QComboBox::currentIndexChanged), this, &DesktopGridEffectConfig::desktopLayoutSelectionChanged); - connect(m_ui.desktopNameAlignmentCombo, qOverload(&QComboBox::currentIndexChanged), this, &KCModule::markAsChanged); - connect(m_ui.shortcutEditor, &KShortcutsEditor::keyChange, this, &KCModule::markAsChanged); -} - -DesktopGridEffectConfig::~DesktopGridEffectConfig() -{ - // If save() is called undo() has no effect - m_ui.shortcutEditor->undo(); -} - -void DesktopGridEffectConfig::save() -{ - m_ui.shortcutEditor->save(); - DesktopGridConfig::setDesktopNameAlignment(m_ui.desktopNameAlignmentCombo->itemData(m_ui.desktopNameAlignmentCombo->currentIndex()).toInt()); - KCModule::save(); - DesktopGridConfig::self()->save(); - - OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), - QStringLiteral("/Effects"), - QDBusConnection::sessionBus()); - interface.reconfigureEffect(QStringLiteral("desktopgrid")); -} - -void DesktopGridEffectConfig::load() -{ - KCModule::load(); - m_ui.desktopNameAlignmentCombo->setCurrentIndex(m_ui.desktopNameAlignmentCombo->findData(QVariant(DesktopGridConfig::desktopNameAlignment()))); - - desktopLayoutSelectionChanged(); -} - -void DesktopGridEffectConfig::desktopLayoutSelectionChanged() -{ - if (m_ui.kcfg_DesktopLayoutMode->currentIndex() == int(DesktopGridEffect::DesktopLayoutMode::LayoutCustom)) { - m_ui.layoutRowsLabel->setEnabled(true); - m_ui.kcfg_CustomLayoutRows->setEnabled(true); - } else { - m_ui.layoutRowsLabel->setEnabled(false); - m_ui.kcfg_CustomLayoutRows->setEnabled(false); - } -} - -void DesktopGridEffectConfig::defaults() -{ - KCModule::defaults(); - m_ui.desktopNameAlignmentCombo->setCurrentIndex(0); -} - -} // namespace - -#include "desktopgrid_config.moc" - -#include "moc_desktopgrid_config.cpp" diff --git a/src/plugins/desktopgrid/desktopgrid_config.h b/src/plugins/desktopgrid/desktopgrid_config.h deleted file mode 100644 index b90f9acf61..0000000000 --- a/src/plugins/desktopgrid/desktopgrid_config.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2007 Rivo Laks - SPDX-FileCopyrightText: 2008 Lucas Murray - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -#include - -#include "desktopgrideffect.h" -#include "ui_desktopgrid_config.h" - -namespace KWin -{ - -class DesktopGridEffectConfigForm : public QWidget, public Ui::DesktopGridEffectConfigForm -{ - Q_OBJECT -public: - explicit DesktopGridEffectConfigForm(QWidget *parent); -}; - -class DesktopGridEffectConfig : public KCModule -{ - Q_OBJECT -public: - explicit DesktopGridEffectConfig(QObject *parent, const KPluginMetaData &data); - ~DesktopGridEffectConfig() override; - -public Q_SLOTS: - void save() override; - void load() override; - void defaults() override; - -private Q_SLOTS: - void desktopLayoutSelectionChanged(); - -private: - DesktopGridEffectConfigForm m_ui; - KActionCollection *m_actionCollection; -}; - -} // namespace diff --git a/src/plugins/desktopgrid/desktopgrid_config.ui b/src/plugins/desktopgrid/desktopgrid_config.ui deleted file mode 100644 index 27eaebfc72..0000000000 --- a/src/plugins/desktopgrid/desktopgrid_config.ui +++ /dev/null @@ -1,206 +0,0 @@ - - - KWin::DesktopGridEffectConfigForm - - - - 0 - 0 - 574 - 312 - - - - - - - Appearance - - - - - - - 0 - 0 - - - - - Pager - - - - - Automatic - - - - - Custom - - - - - - - - N&umber of rows: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - kcfg_CustomLayoutRows - - - - - - - - 0 - 0 - - - - 1 - - - 20 - - - 2 - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - 0 - 0 - - - - - - - - Desktop &name alignment: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - desktopNameAlignmentCombo - - - - - - - Show buttons to alter count of virtual desktops - - - - - - - &Grid layout mode: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - kcfg_DesktopLayoutMode - - - - - - - - Closest - - - - - Natural - - - - - None - - - - - - - - Windows layout: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - kcfg_LayoutMode - - - - - - - - - - Activation - - - - - - - 0 - 0 - - - - - - - - - - - - KShortcutsEditor - QWidget -
kshortcutseditor.h
- 1 -
-
- - desktopNameAlignmentCombo - kcfg_DesktopLayoutMode - kcfg_LayoutMode - kcfg_CustomLayoutRows - kcfg_ShowAddRemove - - - -
diff --git a/src/plugins/desktopgrid/desktopgridconfig.kcfg b/src/plugins/desktopgrid/desktopgridconfig.kcfg deleted file mode 100644 index 1d7887d8df..0000000000 --- a/src/plugins/desktopgrid/desktopgridconfig.kcfg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - 0 - - - 1 - - - 0 - - - 2 - - - true - - - diff --git a/src/plugins/desktopgrid/desktopgridconfig.kcfgc b/src/plugins/desktopgrid/desktopgridconfig.kcfgc deleted file mode 100644 index 4be14ece43..0000000000 --- a/src/plugins/desktopgrid/desktopgridconfig.kcfgc +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Vlad Zahorodnii -# -# SPDX-License-Identifier: CC0-1.0 - -File=desktopgridconfig.kcfg -ClassName=DesktopGridConfig -NameSpace=KWin -Singleton=true -Mutators=true diff --git a/src/plugins/desktopgrid/desktopgrideffect.cpp b/src/plugins/desktopgrid/desktopgrideffect.cpp deleted file mode 100644 index 446d736596..0000000000 --- a/src/plugins/desktopgrid/desktopgrideffect.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "desktopgrideffect.h" -#include "desktopgridconfig.h" - -#include -#include -#include -#include - -#include -#include - -namespace KWin -{ - -DesktopGridEffect::DesktopGridEffect() - : m_shutdownTimer(new QTimer(this)) - , m_state(new EffectTogglableState(this)) - , m_border(new EffectTogglableTouchBorder(m_state)) -{ - qmlRegisterUncreatableType("org.kde.kwin.private.desktopgrid", 1, 0, "DesktopGridEffect", QStringLiteral("Cannot create elements of type DesktopGridEffect")); - - m_shutdownTimer->setSingleShot(true); - connect(m_shutdownTimer, &QTimer::timeout, this, &DesktopGridEffect::realDeactivate); - connect(effects, &EffectsHandler::screenAboutToLock, this, &DesktopGridEffect::realDeactivate); - connect(effects, &EffectsHandler::desktopGridWidthChanged, this, &DesktopGridEffect::gridColumnsChanged); - connect(effects, &EffectsHandler::desktopGridHeightChanged, this, &DesktopGridEffect::gridRowsChanged); - connect(m_state, &EffectTogglableState::activated, this, &DesktopGridEffect::activate); - connect(m_state, &EffectTogglableState::deactivated, this, &DesktopGridEffect::deactivate); - connect(m_state, &EffectTogglableState::inProgressChanged, this, &DesktopGridEffect::gestureInProgressChanged); - connect(m_state, &EffectTogglableState::partialActivationFactorChanged, this, &DesktopGridEffect::partialActivationFactorChanged); - connect(m_state, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { - setRunning(status != EffectTogglableState::Status::Inactive); - }); - - auto toggleAction = m_state->toggleAction(); - toggleAction->setObjectName(QStringLiteral("ShowDesktopGrid")); - toggleAction->setText(i18n("Show Desktop Grid")); - KGlobalAccel::self()->setDefaultShortcut(toggleAction, QList() << (Qt::META | Qt::Key_F8)); - KGlobalAccel::self()->setShortcut(toggleAction, QList() << (Qt::META | Qt::Key_F8)); - m_toggleShortcut = KGlobalAccel::self()->shortcut(toggleAction); - - auto gesture = new EffectTogglableGesture(m_state); - gesture->addTouchpadSwipeGesture(SwipeDirection::Up, 4); - - initConfig(); - reconfigure(ReconfigureAll); - - setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/desktopgrid/qml/main.qml")))); -} - -DesktopGridEffect::~DesktopGridEffect() -{ -} - -void DesktopGridEffect::reconfigure(ReconfigureFlags) -{ - DesktopGridConfig::self()->read(); - setLayout(DesktopGridConfig::layoutMode()); - setAnimationDuration(animationTime(300)); - - for (const ElectricBorder &border : std::as_const(m_borderActivate)) { - effects->unreserveElectricBorder(border, this); - } - - m_borderActivate.clear(); - - const QList activateBorders = DesktopGridConfig::borderActivate(); - for (const int &border : activateBorders) { - m_borderActivate.append(ElectricBorder(border)); - effects->reserveElectricBorder(ElectricBorder(border), this); - } - - m_border->setBorders(DesktopGridConfig::touchBorderActivate()); - - Q_EMIT showAddRemoveChanged(); - Q_EMIT desktopNameAlignmentChanged(); - Q_EMIT desktopLayoutModeChanged(); - Q_EMIT customLayoutRowsChanged(); -} - -int DesktopGridEffect::requestedEffectChainPosition() const -{ - return 70; -} - -bool DesktopGridEffect::borderActivated(ElectricBorder border) -{ - if (m_borderActivate.contains(border)) { - m_state->toggle(); - return true; - } - return false; -} - -void DesktopGridEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent) -{ - if (m_toggleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { - if (keyEvent->type() == QEvent::KeyPress) { - m_state->toggle(); - } - return; - } - QuickSceneEffect::grabbedKeyboardEvent(keyEvent); -} - -Qt::AlignmentFlag DesktopGridEffect::desktopNameAlignment() const -{ - return Qt::AlignmentFlag(DesktopGridConfig::desktopNameAlignment()); -} - -DesktopGridEffect::DesktopLayoutMode DesktopGridEffect::desktopLayoutMode() const -{ - return DesktopGridEffect::DesktopLayoutMode(DesktopGridConfig::desktopLayoutMode()); -} - -int DesktopGridEffect::customLayoutRows() const -{ - return DesktopGridConfig::customLayoutRows(); -} - -void DesktopGridEffect::addDesktop() const -{ - effects->setNumberOfDesktops(effects->numberOfDesktops() + 1); -} - -void DesktopGridEffect::removeDesktop() const -{ - effects->setNumberOfDesktops(effects->numberOfDesktops() - 1); -} - -void DesktopGridEffect::swapDesktops(int from, int to) -{ - QList fromList; - QList toList; - for (auto *w : effects->stackingOrder()) { - if (!w->isNormalWindow() || !w->isOnCurrentActivity() ) { - continue; - } - if (w->isOnDesktop(from)) { - fromList << w; - } else if (w->isOnDesktop(to)) { - toList << w; - } - } - for (auto *w : fromList) { - effects->windowToDesktop(w, to); - } - for (auto *w : toList) { - effects->windowToDesktop(w, from); - } -} - -int DesktopGridEffect::gridRows() const -{ - switch (desktopLayoutMode()) { - case DesktopLayoutMode::LayoutAutomatic: - return ceil(sqrt(effects->numberOfDesktops())); - case DesktopLayoutMode::LayoutCustom: - return std::clamp(customLayoutRows(), 1, effects->numberOfDesktops()); - case DesktopLayoutMode::LayoutPager: - default: - return effects->desktopGridSize().height(); - } -} - -int DesktopGridEffect::gridColumns() const -{ - switch (desktopLayoutMode()) { - case DesktopLayoutMode::LayoutAutomatic: - return ceil(sqrt(effects->numberOfDesktops())); - case DesktopLayoutMode::LayoutCustom: - return std::max(1.0, ceil(qreal(effects->numberOfDesktops()) / customLayoutRows())); - case DesktopLayoutMode::LayoutPager: - default: - return effects->desktopGridSize().width(); - } -} - -int DesktopGridEffect::animationDuration() const -{ - return m_animationDuration; -} - -void DesktopGridEffect::setAnimationDuration(int duration) -{ - if (m_animationDuration != duration) { - m_animationDuration = duration; - Q_EMIT animationDurationChanged(); - } -} - -bool DesktopGridEffect::showAddRemove() const -{ - return DesktopGridConfig::showAddRemove(); -} - -int DesktopGridEffect::layout() const -{ - return m_layout; -} - -void DesktopGridEffect::setLayout(int layout) -{ - if (m_layout != layout) { - m_layout = layout; - Q_EMIT layoutChanged(); - } -} - -bool DesktopGridEffect::gestureInProgress() const -{ - return m_state->inProgress(); -} - -void DesktopGridEffect::activate() -{ - if (effects->isScreenLocked()) { - return; - } - - m_state->activate(); -} - -void DesktopGridEffect::deactivate() -{ - const auto screens = effects->screens(); - for (const auto screen : screens) { - if (QuickSceneView *view = viewForScreen(screen)) { - QMetaObject::invokeMethod(view->rootItem(), "stop"); - } - } - m_shutdownTimer->start(animationDuration()); - - m_state->deactivate(); -} - -void DesktopGridEffect::realDeactivate() -{ - m_state->setStatus(EffectTogglableState::Status::Inactive); -} - -} // namespace KWin - -#include "moc_desktopgrideffect.cpp" diff --git a/src/plugins/desktopgrid/desktopgrideffect.h b/src/plugins/desktopgrid/desktopgrideffect.h deleted file mode 100644 index bc8325b21f..0000000000 --- a/src/plugins/desktopgrid/desktopgrideffect.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -#include "libkwineffects/effecttogglablestate.h" -#include "libkwineffects/kwinquickeffect.h" - -namespace KWin -{ - -class DesktopGridEffect : public QuickSceneEffect -{ - Q_OBJECT - Q_PROPERTY(int gridRows READ gridRows NOTIFY gridRowsChanged) - Q_PROPERTY(int gridColumns READ gridColumns NOTIFY gridColumnsChanged) - Q_PROPERTY(int animationDuration READ animationDuration NOTIFY animationDurationChanged) - Q_PROPERTY(int layout READ layout NOTIFY layoutChanged) - Q_PROPERTY(qreal partialActivationFactor READ partialActivationFactor NOTIFY partialActivationFactorChanged) - Q_PROPERTY(bool gestureInProgress READ gestureInProgress NOTIFY gestureInProgressChanged) - Q_PROPERTY(bool showAddRemove READ showAddRemove NOTIFY showAddRemoveChanged) - Q_PROPERTY(Qt::AlignmentFlag desktopNameAlignment READ desktopNameAlignment NOTIFY desktopNameAlignmentChanged) - Q_PROPERTY(DesktopLayoutMode desktopLayoutMode READ desktopLayoutMode NOTIFY desktopLayoutModeChanged) - Q_PROPERTY(int customLayoutRows READ customLayoutRows NOTIFY customLayoutRowsChanged) - -public: - enum class DesktopLayoutMode { - LayoutPager, - LayoutAutomatic, - LayoutCustom - }; - Q_ENUM(DesktopLayoutMode) - - DesktopGridEffect(); - ~DesktopGridEffect() override; - - int layout() const; - void setLayout(int layout); - - int animationDuration() const; - void setAnimationDuration(int duration); - - bool showAddRemove() const; - - qreal partialActivationFactor() const - { - return m_state->partialActivationFactor(); - } - - bool gestureInProgress() const; - - int gridRows() const; - int gridColumns() const; - - int requestedEffectChainPosition() const override; - bool borderActivated(ElectricBorder border) override; - void reconfigure(ReconfigureFlags flags) override; - void grabbedKeyboardEvent(QKeyEvent *keyEvent) override; - - Qt::AlignmentFlag desktopNameAlignment() const; - DesktopLayoutMode desktopLayoutMode() const; - int customLayoutRows() const; - - Q_INVOKABLE void addDesktop() const; - Q_INVOKABLE void removeDesktop() const; - Q_INVOKABLE void swapDesktops(int from, int to); - -public Q_SLOTS: - void activate(); - void deactivate(); - -Q_SIGNALS: - void gridRowsChanged(); - void gridColumnsChanged(); - void animationDurationChanged(); - void layoutChanged(); - void partialActivationFactorChanged(); - void gestureInProgressChanged(); - void showAddRemoveChanged(); - void desktopNameAlignmentChanged(); - void desktopLayoutModeChanged(); - void customLayoutRowsChanged(); - -private: - void realDeactivate(); - - QTimer *m_shutdownTimer; - QList m_toggleShortcut; - QList m_borderActivate; - EffectTogglableState *const m_state; - EffectTogglableTouchBorder *const m_border; - int m_animationDuration = 400; - int m_layout = 1; -}; - -} // namespace KWin diff --git a/src/plugins/desktopgrid/main.cpp b/src/plugins/desktopgrid/main.cpp deleted file mode 100644 index fe2d52ec86..0000000000 --- a/src/plugins/desktopgrid/main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - SPDX-FileCopyrightText: 2022 Marco Martin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "desktopgrideffect.h" - -namespace KWin -{ - -KWIN_EFFECT_FACTORY_SUPPORTED(DesktopGridEffect, - "metadata.json.stripped", - return DesktopGridEffect::supported();) - -} // namespace KWin - -#include "main.moc" diff --git a/src/plugins/desktopgrid/metadata.json b/src/plugins/desktopgrid/metadata.json deleted file mode 100644 index 7ce2767c5f..0000000000 --- a/src/plugins/desktopgrid/metadata.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "KPlugin": { - "Category": "Window Management", - "Description": "Zoom out so all desktops are displayed side-by-side in a grid", - "Description[ar]": "بعّد لتُعرَض كلّ أسطح المكتب واحدة بجانب الأخرى في شبكة", - "Description[az]": "Bütün İş Masaların bir ekranda yan yana göstərmək", - "Description[be]": "Маштаб памяншаецца, і ўсе працоўныя сталы паказваюцца побач адзін з адным у сетцы", - "Description[bg]": "Намаляване на мащаба, докато всички работни плотове се представят като плочки един до друг", - "Description[ca@valencia]": "Reduïx tots els escriptoris per a visualitzar-los un al costat de l'altre en una quadrícula", - "Description[ca]": "Redueix tots els escriptoris per a visualitzar-los un al costat de l'altre en una quadrícula", - "Description[cs]": "Oddálí plochy a zobrazí je v mřížce", - "Description[de]": "Verkleinert die Arbeitsflächen, sodass sie in einem Raster nebeneinander zu sehen sind", - "Description[en_GB]": "Zoom out so all desktops are displayed side-by-side in a grid", - "Description[eo]": "Malzomi por ke ĉiuj labortabloj estu montrataj flank-al-flanke en krado", - "Description[es]": "Reduce la ampliación para que todos los escritorios se muestren uno al lado del otro en una cuadrícula", - "Description[et]": "Vähendamine, et kõik töölauad oleksid üksteise kõrval võrgustikus näha", - "Description[eu]": "Zooma urrundu mahaigain guztiak sareta batean bata bestearen ondoan ager daitezen", - "Description[fi]": "Loitontaa työpöytiä niin, että ne näytetään rinnakkain ruudukkona", - "Description[fr]": "Faire un zoom arrière de manière à afficher tous les bureaux côte à côte dans une grille", - "Description[hu]": "Kinagyítja az asztalt oly módon, hogy a virtuális asztalok rácsban elrendezve jelennek meg", - "Description[ia]": "Face zoom retro assi que omne scriptorios es monstrate flanco a flanco in un grillia", - "Description[id]": "Zoom keluar sehingga semua desktop ditampilkan sisi demi sisi di dalam kisi", - "Description[it]": "Arretra per mostrare tutti i desktop virtuali affiancati in una griglia", - "Description[ja]": "ズームアウトしてすべてのデスクトップを並べて表示します", - "Description[ka]": "დაპატარავება ისე, რომ ყველა სამუშაო მაგიდა გვერდიგვერდ, ბადეში იქნება ნაჩვენები", - "Description[ko]": "모든 바탕 화면을 한 화면에 볼 수 있도록 격자형으로 축소합니다", - "Description[nl]": "Verkleint alle bureaubladen zodat ze zij-aan-zij in een raster getoond kunnen worden", - "Description[nn]": "Forminsk skjermflata slik at alle skriveborda vert viste side om side", - "Description[pl]": "Pomniejsza do chwili, aż wszystkie pulpity będą widoczne obok siebie na siatce", - "Description[pt]": "Reduz o ecrã de forma a mostrar todos os ecrãs lado-a-lado numa grelha", - "Description[pt_BR]": "Reduz para que todas as áreas de trabalho sejam mostradas lado a lado em uma grade", - "Description[ro]": "Îndepărtează astfel încât toate birourile sunt afișate alăturat într-o grilă", - "Description[ru]": "Просмотр всех рабочих столов на одном экране", - "Description[sk]": "Oddiali všetky plochy a zobrazí ich vedľa seba v mriežke", - "Description[sl]": "Oddalji pogled tako, da so vsa namizja prikazana drugo ob drugem v mreži", - "Description[sv]": "Zooma ut så att alla skrivbord visas sida vid sida i ett rutnät", - "Description[ta]": "அனைத்து பணிமேடைகளை வரிசைகளிலும் நெடுவரிசைகளிலும் காட்டும்", - "Description[tr]": "Uzaklaştır, böylece tüm masaüstleri bir ızgarada yan yana görüntülenirler", - "Description[uk]": "Зменшення стільниць так, щоб всі стільниці було показано поруч у форматі таблиці", - "Description[vi]": "Thu nhỏ để tất cả các bàn làm việc được hiển thị cạnh nhau trong một lưới", - "Description[x-test]": "xxZoom out so all desktops are displayed side-by-side in a gridxx", - "Description[zh_CN]": "所有虚拟桌面缩小后显示在一套网格中", - "Description[zh_TW]": "縮小顯示讓所有桌面都同時顯示在格線內", - "EnabledByDefault": true, - "License": "GPL", - "Name": "Desktop Grid", - "Name[ar]": "شبكة سطح المكتب", - "Name[az]": "İş masası toru", - "Name[be]": "Сетка для працоўных сталоў", - "Name[bg]": "Плочки", - "Name[ca@valencia]": "Quadrícula de l'escriptori", - "Name[ca]": "Quadrícula de l'escriptori", - "Name[cs]": "Mřížka plochy", - "Name[de]": "Arbeitsflächen-Umschalter (Raster)", - "Name[en_GB]": "Desktop Grid", - "Name[eo]": "Labortabla Krado", - "Name[es]": "Rejilla del escritorio", - "Name[et]": "Töölauavõrgustik", - "Name[eu]": "Mahaigain sareta", - "Name[fi]": "Työpöydän ruudukko", - "Name[fr]": "Grille de bureaux", - "Name[hu]": "Asztalrács", - "Name[ia]": "Grillia de scriptorio", - "Name[id]": "Kisi Desktop", - "Name[it]": "Griglia dei Desktop", - "Name[ja]": "デスクトップグリッド", - "Name[ka]": "სამუშაო მაგიდის ბადე", - "Name[ko]": "바탕 화면 격자", - "Name[nl]": "Bureaubladraster", - "Name[nn]": "Skrivebordsoversikt", - "Name[pl]": "Siatka pulpitu", - "Name[pt]": "Grelha de Ecrãs", - "Name[pt_BR]": "Grade de áreas de trabalho", - "Name[ro]": "Grilă de birou", - "Name[ru]": "Все рабочие столы", - "Name[sk]": "Plochy v mriežke", - "Name[sl]": "Mreža namizja", - "Name[sv]": "Skrivbordsrutnät", - "Name[ta]": "பணிமேடை கிரிட்", - "Name[tr]": "Masaüstü Izgarası", - "Name[uk]": "Таблиця стільниць", - "Name[vi]": "Lưới bàn làm việc", - "Name[x-test]": "xxDesktop Gridxx", - "Name[zh_CN]": "虚拟桌面平铺网格", - "Name[zh_TW]": "桌面格線" - }, - "X-KDE-ConfigModule": "kwin_desktopgrid_config", - "X-KWin-Border-Activate": true, - "org.kde.kwin.effect": { - "video": "https://files.kde.org/plasma/kwin/effect-videos/desktop_grid.mp4" - } -} diff --git a/src/plugins/desktopgrid/qml/DesktopView.qml b/src/plugins/desktopgrid/qml/DesktopView.qml deleted file mode 100644 index a0ea3ea57e..0000000000 --- a/src/plugins/desktopgrid/qml/DesktopView.qml +++ /dev/null @@ -1,306 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - SPDX-FileCopyrightText: 2022 Marco Martin - SPDX-FileCopyrightText: 2022 ivan tkachenko - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -import QtQuick -import org.kde.kwin as KWinComponents -import org.kde.kwin.private.effects -import org.kde.kirigami 2.20 as Kirigami -import org.kde.plasma.components 3.0 as PC3 -import org.kde.kwin.private.desktopgrid - -FocusScope { - id: desktopView - - required property QtObject windowModel - required property QtObject desktop - required property var dndManagerStore - readonly property bool dragActive: heap.dragActive || dragHandler.active || xAnim.running || yAnim.running - property real panelOpacity: 1 - focus: true - - function selectLastItem(direction) { - heap.selectLastItem(direction); - } - - DropArea { - anchors.fill: parent - onEntered: { - drag.accepted = true; - } - onDropped: drop => { - drop.accepted = true; - if (drag.source instanceof DesktopView) { - // dragging a desktop as a whole - if (drag.source === desktopView) { - drop.action = Qt.IgnoreAction; - return; - } - effect.swapDesktops(drag.source.desktop.x11DesktopNumber, desktop.x11DesktopNumber); - } else { - // dragging a KWin::Window - if (drag.source.desktops.length === 0 || drag.source.desktops.indexOf(desktopView.desktop) !== -1) { - drop.action = Qt.IgnoreAction; - return; - } - drag.source.desktops = [desktopView.desktop]; - } - } - } - Connections { - target: effect - function onItemDroppedOutOfScreen(globalPos, item, screen) { - if (screen !== targetScreen) { - return; - } - const pos = screen.mapFromGlobal(globalPos); - if (!desktopView.contains(desktopView.mapFromItem(null, pos.x, pos.y))) { - return; - } - item.client.desktops = [desktopView.desktop]; - } - } - Repeater { - model: KWinComponents.WindowFilterModel { - activity: KWinComponents.Workspace.currentActivity - desktop: desktopView.desktop - screenName: targetScreen.name - windowModel: desktopView.windowModel - windowType: KWinComponents.WindowFilterModel.Dock | KWinComponents.WindowFilterModel.Desktop - } - - KWinComponents.WindowThumbnail { - wId: model.window.internalId - x: model.window.x - targetScreen.geometry.x - y: model.window.y - targetScreen.geometry.y - z: model.window.stackingOrder - width: model.window.width - height: model.window.height - opacity: model.window.dock ? desktopView.panelOpacity : 1 - } - } - - DragHandler { - id: dragHandler - target: heap - grabPermissions: PointerHandler.ApprovesTakeOverByHandlersOfSameType - onActiveChanged: { - if (!active) { - heap.Drag.drop(); - Qt.callLater(heap.resetPosition) - } - } - } - - WindowHeap { - id: heap - function resetPosition() { - x = 0; - y = 0; - } - Drag.active: dragHandler.active - Drag.proposedAction: Qt.MoveAction - Drag.supportedActions: Qt.MoveAction - Drag.source: desktopView - Drag.hotSpot: Qt.point(width * 0.5, height * 0.5) - width: parent.width - height: parent.height - focus: true - z: 9999 - animationDuration: container.effect.animationDuration - absolutePositioning: false - animationEnabled: container.animationEnabled - organized: container.organized - layout.mode: effect.layout - dndManagerStore: desktopView.dndManagerStore - model: KWinComponents.WindowFilterModel { - activity: KWinComponents.Workspace.currentActivity - desktop: desktopView.desktop - screenName: targetScreen.name - windowModel: desktopView.windowModel - windowType: ~KWinComponents.WindowFilterModel.Dock & - ~KWinComponents.WindowFilterModel.Desktop & - ~KWinComponents.WindowFilterModel.Notification & - ~KWinComponents.WindowFilterModel.CriticalNotification - } - delegate: WindowHeapDelegate { - windowHeap: heap - closeButtonVisible: false - windowTitleVisible: false - - TapHandler { - acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen - acceptedButtons: Qt.MiddleButton | Qt.RightButton - onTapped: (eventPoint, button) => { - if (button === Qt.MiddleButton) { - window.closeWindow(); - } else if (button === Qt.RightButton) { - if (window.desktops.length > 0) { - window.desktops = []; - } else { - window.desktops = [desktopView.desktop]; - } - } - } - } - } - onActivated: effect.deactivate(effect.animationDuration); - Behavior on x { - enabled: !dragHandler.active - NumberAnimation { - id: xAnim - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - Behavior on y { - enabled: !dragHandler.active - NumberAnimation { - id: yAnim - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: heap - acceptedButtons: Qt.NoButton - cursorShape: dragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor - } - - PC3.Control { - id: desktopLabel - anchors.margins: Kirigami.Units.gridUnit - z: 9999 - visible: effect.desktopNameAlignment !== 0 - - states: [ - State { - when: container.effect.desktopNameAlignment === (Qt.AlignTop | Qt.AlignLeft) - AnchorChanges { - target: desktopLabel - anchors.left: desktopView.left - anchors.top: desktopView.top - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.TopLeft - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignTop | Qt.AlignHCenter) - AnchorChanges { - target: desktopLabel - anchors.top: desktopView.top - anchors.horizontalCenter: desktopView.horizontalCenter - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.Top - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignTop | Qt.AlignRight) - AnchorChanges { - target: desktopLabel - anchors.top: desktopView.top - anchors.right: desktopView.right - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.TopRight - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignVCenter | Qt.AlignLeft) - AnchorChanges { - target: desktopLabel - anchors.left: desktopView.left - anchors.verticalCenter: desktopView.verticalCenter - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.Left - } - }, - State { - when: container.effect.desktopNameAlignment === Qt.AlignCenter - AnchorChanges { - target: desktopLabel - anchors.horizontalCenter: desktopView.horizontalCenter - anchors.verticalCenter: desktopView.verticalCenter - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.Center - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignVCenter | Qt.AlignRight) - AnchorChanges { - target: desktopLabel - anchors.verticalCenter: desktopView.verticalCenter - anchors.right: desktopView.right - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.Right - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignBottom | Qt.AlignLeft) - AnchorChanges { - target: desktopLabel - anchors.left: desktopView.left - anchors.bottom: desktopView.bottom - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.BottomLeft - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignBottom | Qt.AlignHCenter) - AnchorChanges { - target: desktopLabel - anchors.bottom: desktopView.bottom - anchors.horizontalCenter: desktopView.horizontalCenter - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.Bottom - } - }, - State { - when: container.effect.desktopNameAlignment === (Qt.AlignBottom | Qt.AlignRight) - AnchorChanges { - target: desktopLabel - anchors.bottom: desktopView.bottom - anchors.right: desktopView.right - } - PropertyChanges { - target: desktopLabel - transformOrigin: Item.BottomRight - } - } - ] - - scale: 1 / desktopView.parent.scale - leftPadding: Kirigami.Units.smallSpacing - rightPadding: Kirigami.Units.smallSpacing - contentItem: PC3.Label { - text: desktopView.desktop.name - textFormat: Text.PlainText - } - background: Rectangle { - color: Kirigami.Theme.backgroundColor - radius: height - opacity: 0.6 - } - } -} diff --git a/src/plugins/desktopgrid/qml/main.qml b/src/plugins/desktopgrid/qml/main.qml deleted file mode 100644 index f13d2a0180..0000000000 --- a/src/plugins/desktopgrid/qml/main.qml +++ /dev/null @@ -1,328 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - SPDX-FileCopyrightText: 2022 Marco Martin - SPDX-FileCopyrightText: 2022 ivan tkachenko - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -import QtQuick -import Qt5Compat.GraphicalEffects -import QtQuick.Layouts -import QtQuick.Window -import org.kde.kwin as KWinComponents -import org.kde.kwin.private.effects -import org.kde.kirigami 2.20 as Kirigami -import org.kde.plasma.components 3.0 as PC3 - -Rectangle { - id: container - - readonly property QtObject effect: KWinComponents.SceneView.effect - readonly property QtObject targetScreen: KWinComponents.SceneView.screen - - property bool animationEnabled: false - property bool organized: false - - /** Shared Drag&Drop store to keep track of DND state across desktops. */ - property var dndManagerStore: ({}) - - color: "black" - - function start() { - animationEnabled = true; - organized = true; - } - - function stop() { - organized = false; - } - - function switchTo(desktop) { - KWinComponents.Workspace.currentDesktop = desktop; - effect.deactivate(); - } - - function selectNext(direction) { - let currentIndex = 0 - for (let i = 0; i < gridRepeater.count; i++) { - if (gridRepeater.itemAt(i).focus) { - currentIndex = i; - break; - } - } - let x = currentIndex % grid.columns; - let y = Math.floor(currentIndex / grid.columns); - - // the direction we move in is the opposite of the window to select - // i.e pressing left should select the rightmost window on the desktop - // to the left - let invertedDirection; - switch(direction) { - case WindowHeap.Direction.Up: - y--; - invertedDirection = WindowHeap.Direction.Down; - break; - case WindowHeap.Direction.Down: - y++ - invertedDirection = WindowHeap.Direction.Up; - break; - case WindowHeap.Direction.Left: - x--; - invertedDirection = WindowHeap.Direction.Right; - break; - case WindowHeap.Direction.Right: - x++; - invertedDirection = WindowHeap.Direction.Left; - break; - } - - if (x < 0 || x >= grid.columns) { - return false; - } - if (y < 0 || y >= grid.rows) { - return false; - } - let newIndex = y * grid.columns + x; - - gridRepeater.itemAt(newIndex).focus = true; - gridRepeater.itemAt(newIndex).selectLastItem(invertedDirection); - return true; - } - - Keys.onPressed: { - if (event.key === Qt.Key_Escape) { - effect.deactivate(); - } else if (event.key === Qt.Key_Plus || event.key === Qt.Key_Equal) { - addButton.clicked(); - } else if (event.key === Qt.Key_Minus) { - removeButton.clicked(); - } else if (event.key >= Qt.Key_F1 && event.key <= Qt.Key_F12) { - const desktopId = event.key - Qt.Key_F1; - if (desktopId < gridRepeater.count) { - switchTo(gridRepeater.itemAt(desktopId).desktop); - } - } else if (event.key >= Qt.Key_0 && event.key <= Qt.Key_9) { - const desktopId = event.key === Qt.Key_0 ? 9 : (event.key - Qt.Key_1); - if (desktopId < gridRepeater.count) { - switchTo(gridRepeater.itemAt(desktopId).desktop); - } - } else if (event.key === Qt.Key_Up) { - event.accepted = selectNext(WindowHeap.Direction.Up); - if (!event.accepted) { - let view = effect.getView(Qt.TopEdge) - if (view) { - effect.activateView(view) - } - } - } else if (event.key === Qt.Key_Down) { - event.accepted = selectNext(WindowHeap.Direction.Down); - if (!event.accepted) { - let view = effect.getView(Qt.BottomEdge) - if (view) { - effect.activateView(view) - } - } - } else if (event.key === Qt.Key_Left) { - event.accepted = selectNext(WindowHeap.Direction.Left); - if (!event.accepted) { - let view = effect.getView(Qt.LeftEdge) - if (view) { - effect.activateView(view) - } - } - } else if (event.key === Qt.Key_Right) { - event.accepted = selectNext(WindowHeap.Direction.Right); - if (!event.accepted) { - let view = effect.getView(Qt.RightEdge) - if (view) { - effect.activateView(view) - } - } - } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Space) { - for (let i = 0; i < gridRepeater.count; i++) { - if (gridRepeater.itemAt(i).focus) { - switchTo(gridRepeater.itemAt(i).desktop) - break; - } - } - } - } - Keys.priority: Keys.AfterItem - - KWinComponents.VirtualDesktopModel { - id: desktopModel - } - KWinComponents.WindowModel { - id: stackModel - } - - // A grid, not a gridlayout as a gridlayout positions its elements too late - Grid { - id: grid - - property Item currentItem - readonly property real targetScale: Math.min(parent.width / width, parent.height / height) - property real panelOpacity: 1 - - Behavior on x { - enabled: !container.effect.gestureInProgress - NumberAnimation { - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - Behavior on y { - enabled: !container.effect.gestureInProgress - NumberAnimation { - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - Behavior on scale { - enabled: !container.effect.gestureInProgress - NumberAnimation { - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - Behavior on panelOpacity { - enabled: !container.effect.gestureInProgress - NumberAnimation { - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - - width: (parent.width + columnSpacing) * columns - columnSpacing - height: (parent.height + rowSpacing) * rows - rowSpacing - rowSpacing: Kirigami.Units.gridUnit - columnSpacing: Kirigami.Units.gridUnit - rows: container.effect.gridRows - columns: container.effect.gridColumns - transformOrigin: Item.TopLeft - - states: [ - State { - when: container.effect.gestureInProgress - PropertyChanges { - target: grid - x: Math.max(0, container.width / 2 - (grid.width * grid.targetScale) / 2) * container.effect.partialActivationFactor - grid.currentItem.x * (1 - container.effect.partialActivationFactor) - y: Math.max(0, container.height / 2 - (grid.height * grid.targetScale) / 2) * container.effect.partialActivationFactor - grid.currentItem.y * (1 - container.effect.partialActivationFactor) - scale: 1 - (1 - grid.targetScale) * container.effect.partialActivationFactor - panelOpacity: 1 - container.effect.partialActivationFactor - } - PropertyChanges { - target: buttonsLayout - opacity: container.effect.partialActivationFactor - } - }, - State { - when: container.organized - PropertyChanges { - target: grid - x: Math.max(0, container.width / 2 - (grid.width * grid.targetScale) / 2) - y: Math.max(0, container.height / 2 - (grid.height * grid.targetScale) / 2) - scale: grid.targetScale - panelOpacity: 0 - } - PropertyChanges { - target: buttonsLayout - opacity: 1 - } - }, - State { - when: !container.organized - PropertyChanges { - target: grid - x: -grid.currentItem.x - y: -grid.currentItem.y - scale: 1 - panelOpacity: 1 - } - PropertyChanges { - target: buttonsLayout - opacity: 0 - } - } - ] - Repeater { - id: gridRepeater - model: desktopModel - DesktopView { - id: thumbnail - - panelOpacity: grid.panelOpacity - readonly property bool current: KWinComponents.Workspace.currentDesktop === desktop - z: dragActive ? 1 : 0 - onCurrentChanged: { - if (current) { - grid.currentItem = thumbnail; - } - } - Component.onCompleted: { - if (current) { - grid.currentItem = thumbnail; - } - } - width: container.width - height: container.height - - windowModel: stackModel - dndManagerStore: container.dndManagerStore - Rectangle { - anchors.fill: parent - color: "transparent" - border { - color: Kirigami.Theme.highlightColor - width: 1 / grid.scale - } - visible: parent.activeFocus - } - TapHandler { - acceptedButtons: Qt.LeftButton - onTapped: { - KWinComponents.Workspace.currentDesktop = thumbnail.desktop; - container.effect.deactivate(); - } - } - } - } - } - - RowLayout { - id: buttonsLayout - anchors { - right: parent.right - bottom: parent.bottom - margins: Kirigami.Units.smallSpacing - } - spacing: Kirigami.Units.smallSpacing - visible: container.effect.showAddRemove - PC3.Button { - id: addButton - icon.name: "list-add" - onClicked: container.effect.addDesktop() - } - PC3.Button { - id: removeButton - icon.name: "list-remove" - onClicked: container.effect.removeDesktop() - } - Behavior on opacity { - enabled: !container.effect.gestureInProgress - NumberAnimation { - duration: container.effect.animationDuration - easing.type: Easing.OutCubic - } - } - } - TapHandler { - acceptedButtons: Qt.LeftButton - onTapped: { - container.effect.deactivate(); - } - } - - Component.onCompleted: start() -} diff --git a/src/plugins/overview/kcm/overvieweffectkcm.cpp b/src/plugins/overview/kcm/overvieweffectkcm.cpp index 06521b4002..f0d4b00337 100644 --- a/src/plugins/overview/kcm/overvieweffectkcm.cpp +++ b/src/plugins/overview/kcm/overvieweffectkcm.cpp @@ -36,12 +36,33 @@ OverviewEffectConfig::OverviewEffectConfig(QObject *parent, const KPluginMetaDat actionCollection->setConfigGroup(QStringLiteral("Overview")); actionCollection->setConfigGlobal(true); - const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_W; - QAction *toggleAction = actionCollection->addAction(QStringLiteral("Overview")); - toggleAction->setText(i18n("Toggle Overview")); - toggleAction->setProperty("isConfigurationAction", true); - KGlobalAccel::self()->setDefaultShortcut(toggleAction, {defaultToggleShortcut}); - KGlobalAccel::self()->setShortcut(toggleAction, {defaultToggleShortcut}); + const QKeySequence defaultCycleShortcut = Qt::META | Qt::Key_Tab; + QAction *cycleAction = actionCollection->addAction(QStringLiteral("Cycle Overview")); + cycleAction->setText(i18nc("@action Overview and Grid View are the name of KWin effects", "Cycle through Overview and Grid View")); + cycleAction->setProperty("isConfigurationAction", true); + KGlobalAccel::self()->setDefaultShortcut(cycleAction, {defaultCycleShortcut}); + KGlobalAccel::self()->setShortcut(cycleAction, {defaultCycleShortcut}); + + const QKeySequence defaultUncycleShortcut = Qt::META | Qt::SHIFT | Qt::Key_Tab; + QAction *reverseCycleAction = actionCollection->addAction(QStringLiteral("Cycle Overview Opposite")); + reverseCycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Grid View and Overview")); + reverseCycleAction->setProperty("isConfigurationAction", true); + KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {defaultUncycleShortcut}); + KGlobalAccel::self()->setShortcut(reverseCycleAction, {defaultUncycleShortcut}); + + const QKeySequence defaultOverviewShortcut = Qt::META | Qt::Key_W; + QAction *overviewAction = actionCollection->addAction(QStringLiteral("Overview")); + overviewAction->setText(i18nc("@action Overview is the name of a KWin effect", "Toggle Overview")); + overviewAction->setProperty("isConfigurationAction", true); + KGlobalAccel::self()->setDefaultShortcut(overviewAction, {defaultOverviewShortcut}); + KGlobalAccel::self()->setShortcut(overviewAction, {defaultOverviewShortcut}); + + const QKeySequence defaultGridShortcut = Qt::META | Qt::Key_G; + QAction *gridAction = actionCollection->addAction(QStringLiteral("Grid View")); + gridAction->setText(i18nc("@action Grid View is the name of a KWin effect", "Toggle Grid View")); + gridAction->setProperty("isConfigurationAction", true); + KGlobalAccel::self()->setDefaultShortcut(gridAction, {defaultGridShortcut}); + KGlobalAccel::self()->setShortcut(gridAction, {defaultGridShortcut}); ui.shortcutsEditor->addCollection(actionCollection); connect(ui.shortcutsEditor, &KShortcutsEditor::keyChange, this, &KCModule::markAsChanged); diff --git a/src/plugins/overview/kcm/overvieweffectkcm.ui b/src/plugins/overview/kcm/overvieweffectkcm.ui index fffb5805f3..d7eddc32e3 100644 --- a/src/plugins/overview/kcm/overvieweffectkcm.ui +++ b/src/plugins/overview/kcm/overvieweffectkcm.ui @@ -51,7 +51,21 @@ - + + + + Organize windows in the Grid View: + + + + + + + + + + + diff --git a/src/plugins/overview/overviewconfig.kcfg b/src/plugins/overview/overviewconfig.kcfg index 03f4fe0351..d171e68b49 100644 --- a/src/plugins/overview/overviewconfig.kcfg +++ b/src/plugins/overview/overviewconfig.kcfg @@ -16,6 +16,9 @@ false + + true + diff --git a/src/plugins/overview/overvieweffect.cpp b/src/plugins/overview/overvieweffect.cpp index d4440bd039..8b2dc1b380 100644 --- a/src/plugins/overview/overvieweffect.cpp +++ b/src/plugins/overview/overvieweffect.cpp @@ -19,35 +19,137 @@ namespace KWin { OverviewEffect::OverviewEffect() - : m_state(new EffectTogglableState(this)) - , m_border(new EffectTogglableTouchBorder(m_state)) + // manages the transition between inactive -> overview + : m_overviewState(new EffectTogglableState(this)) + // manages the transition between overview -> grid + , m_transitionState(new EffectTogglableState(this)) + // manages the transition betwee inactive -> overview + , m_gridState(new EffectTogglableState(this)) + , m_border(new EffectTogglableTouchBorder(m_overviewState)) , m_shutdownTimer(new QTimer(this)) { - auto gesture = new EffectTogglableGesture(m_state); - gesture->addTouchpadPinchGesture(PinchDirection::Contracting, 4); + auto gesture = new EffectTogglableGesture(m_overviewState); + gesture->addTouchpadSwipeGesture(SwipeDirection::Up, 4); gesture->addTouchscreenSwipeGesture(SwipeDirection::Up, 3); - connect(m_state, &EffectTogglableState::activated, this, &OverviewEffect::activate); - connect(m_state, &EffectTogglableState::deactivated, this, &OverviewEffect::deactivate); - connect(m_state, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::gestureInProgressChanged); - connect(m_state, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::partialActivationFactorChanged); - connect(m_state, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { + auto transitionGesture = new EffectTogglableGesture(m_transitionState); + transitionGesture->addTouchpadSwipeGesture(SwipeDirection::Up, 4); + transitionGesture->addTouchscreenSwipeGesture(SwipeDirection::Up, 3); + m_transitionState->stop(); + + auto gridGesture = new EffectTogglableGesture(m_gridState); + gridGesture->addTouchpadSwipeGesture(SwipeDirection::Down, 4); + gridGesture->addTouchscreenSwipeGesture(SwipeDirection::Down, 3); + + connect(m_overviewState, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::overviewGestureInProgressChanged); + connect(m_overviewState, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::overviewPartialActivationFactorChanged); + + connect(m_overviewState, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { if (status == EffectTogglableState::Status::Activating || status == EffectTogglableState::Status::Active) { m_searchText = QString(); + setRunning(true); + m_gridState->stop(); } - setRunning(status != EffectTogglableState::Status::Inactive); + if (status == EffectTogglableState::Status::Active) { + m_transitionState->deactivate(); + } + if (status == EffectTogglableState::Status::Inactive || status == EffectTogglableState::Status::Deactivating) { + m_transitionState->stop(); + } + if (status == EffectTogglableState::Status::Inactive) { + m_gridState->deactivate(); + deactivate(); + } + }); + + connect(m_transitionState, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { + if (status == EffectTogglableState::Status::Activating || status == EffectTogglableState::Status::Active) { + m_overviewState->stop(); + } + if (status == EffectTogglableState::Status::Inactive) { + m_overviewState->activate(); + } + if (status == EffectTogglableState::Status::Active) { + m_gridState->activate(); + } + if (status == EffectTogglableState::Status::Inactive || status == EffectTogglableState::Status::Deactivating) { + m_gridState->stop(); + } + }); + + connect(m_gridState, &EffectTogglableState::statusChanged, this, [this](EffectTogglableState::Status status) { + if (status == EffectTogglableState::Status::Activating || status == EffectTogglableState::Status::Active) { + m_searchText = QString(); + setRunning(true); + m_overviewState->stop(); + } + if (status == EffectTogglableState::Status::Inactive) { + m_overviewState->deactivate(); + deactivate(); + } + if (status == EffectTogglableState::Status::Active) { + m_transitionState->activate(); + } + if (status == EffectTogglableState::Status::Inactive || status == EffectTogglableState::Status::Deactivating) { + m_transitionState->stop(); + } + }); + + connect(m_transitionState, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::transitionGestureInProgressChanged); + connect(m_transitionState, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::transitionPartialActivationFactorChanged); + + connect(m_gridState, &EffectTogglableState::inProgressChanged, this, &OverviewEffect::gridGestureInProgressChanged); + connect(m_gridState, &EffectTogglableState::partialActivationFactorChanged, this, &OverviewEffect::gridPartialActivationFactorChanged); + + connect(effects, &EffectsHandler::desktopChanging, this, [this](uint old, QPointF desktopOffset, EffectWindow *with) { + m_desktopOffset = desktopOffset; + Q_EMIT desktopOffsetChanged(); + }); + connect(effects, &EffectsHandler::desktopChanged, this, [this](int old, int current, EffectWindow *with) { + m_desktopOffset = QPointF(0, 0); + Q_EMIT desktopOffsetChanged(); + }); + connect(effects, &EffectsHandler::desktopChangingCancelled, this, [this]() { + m_desktopOffset = QPointF(0, 0); + Q_EMIT desktopOffsetChanged(); }); m_shutdownTimer->setSingleShot(true); connect(m_shutdownTimer, &QTimer::timeout, this, &OverviewEffect::realDeactivate); - const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_W; - auto toggleAction = m_state->toggleAction(); - toggleAction->setObjectName(QStringLiteral("Overview")); - toggleAction->setText(i18n("Toggle Overview")); - KGlobalAccel::self()->setDefaultShortcut(toggleAction, {defaultToggleShortcut}); - KGlobalAccel::self()->setShortcut(toggleAction, {defaultToggleShortcut}); - m_toggleShortcut = KGlobalAccel::self()->shortcut(toggleAction); + const QKeySequence defaultCycleShortcut = Qt::META | Qt::Key_Tab; + auto cycleAction = new QAction(this); + connect(cycleAction, &QAction::triggered, this, &OverviewEffect::cycle); + cycleAction->setObjectName(QStringLiteral("Cycle Overview")); + cycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Overview and Grid View")); + KGlobalAccel::self()->setDefaultShortcut(cycleAction, {defaultCycleShortcut}); + KGlobalAccel::self()->setShortcut(cycleAction, {defaultCycleShortcut}); + m_cycleShortcut = KGlobalAccel::self()->shortcut(cycleAction); + + const QKeySequence defaultUncycleShortcut = Qt::META | Qt::SHIFT | Qt::Key_Tab; + auto reverseCycleAction = new QAction(this); + connect(reverseCycleAction, &QAction::triggered, this, &OverviewEffect::reverseCycle); + reverseCycleAction->setObjectName(QStringLiteral("Cycle Overview Opposite")); + reverseCycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Grid View and Overview")); + KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {defaultUncycleShortcut}); + KGlobalAccel::self()->setShortcut(reverseCycleAction, {defaultUncycleShortcut}); + m_reverseCycleShortcut = KGlobalAccel::self()->shortcut(reverseCycleAction); + + const QKeySequence defaultOverviewShortcut = Qt::META | Qt::Key_W; + auto overviewAction = m_overviewState->toggleAction(); + overviewAction->setObjectName(QStringLiteral("Overview")); + overviewAction->setText(i18nc("@action Overview is the name of a Kwin effect", "Toggle Overview")); + KGlobalAccel::self()->setDefaultShortcut(overviewAction, {defaultOverviewShortcut}); + KGlobalAccel::self()->setShortcut(overviewAction, {defaultOverviewShortcut}); + m_overviewShortcut = KGlobalAccel::self()->shortcut(overviewAction); + + const QKeySequence defaultGridShortcut = Qt::META | Qt::Key_G; + auto gridAction = m_gridState->toggleAction(); + gridAction->setObjectName(QStringLiteral("Grid View")); + gridAction->setText(i18nc("@action Grid view is the name of a Kwin effect", "Toggle Grid View")); + KGlobalAccel::self()->setDefaultShortcut(gridAction, {defaultGridShortcut}); + KGlobalAccel::self()->setShortcut(gridAction, {defaultGridShortcut}); + m_overviewShortcut = KGlobalAccel::self()->shortcut(gridAction); connect(effects, &EffectsHandler::screenAboutToLock, this, &OverviewEffect::realDeactivate); @@ -95,6 +197,41 @@ void OverviewEffect::setAnimationDuration(int duration) } } +qreal OverviewEffect::overviewPartialActivationFactor() const +{ + return m_overviewState->partialActivationFactor(); +} + +bool OverviewEffect::overviewGestureInProgress() const +{ + return m_overviewState->inProgress(); +} + +qreal OverviewEffect::transitionPartialActivationFactor() const +{ + return m_transitionState->partialActivationFactor(); +} + +bool OverviewEffect::transitionGestureInProgress() const +{ + return m_transitionState->inProgress(); +} + +qreal OverviewEffect::gridPartialActivationFactor() const +{ + return m_gridState->partialActivationFactor(); +} + +bool OverviewEffect::gridGestureInProgress() const +{ + return m_gridState->inProgress(); +} + +QPointF OverviewEffect::desktopOffset() const +{ + return m_desktopOffset; +} + int OverviewEffect::layout() const { return m_layout; @@ -105,6 +242,11 @@ bool OverviewEffect::ignoreMinimized() const return OverviewConfig::ignoreMinimized(); } +bool OverviewEffect::organizedGrid() const +{ + return OverviewConfig::organizedGrid(); +} + void OverviewEffect::setLayout(int layout) { if (m_layout != layout) { @@ -121,7 +263,7 @@ int OverviewEffect::requestedEffectChainPosition() const bool OverviewEffect::borderActivated(ElectricBorder border) { if (m_borderActivate.contains(border)) { - m_state->toggle(); + cycle(); return true; } return false; @@ -133,7 +275,7 @@ void OverviewEffect::activate() return; } - m_state->activate(); + m_overviewState->activate(); } void OverviewEffect::deactivate() @@ -145,26 +287,89 @@ void OverviewEffect::deactivate() } } m_shutdownTimer->start(animationDuration()); - - m_state->deactivate(); + m_overviewState->deactivate(); } void OverviewEffect::realDeactivate() { - m_state->setStatus(EffectTogglableState::Status::Inactive); + if (m_overviewState->status() == EffectTogglableState::Status::Inactive) { + setRunning(false); + } +} + +void OverviewEffect::cycle() +{ + if (m_overviewState->status() == EffectTogglableState::Status::Inactive) { + m_overviewState->activate(); + } else if (m_transitionState->status() == EffectTogglableState::Status::Inactive) { + m_transitionState->activate(); + } else if (m_gridState->status() == EffectTogglableState::Status::Active) { + m_overviewState->deactivate(); + } +} + +void OverviewEffect::reverseCycle() +{ + if (m_overviewState->status() == EffectTogglableState::Status::Active) { + m_overviewState->deactivate(); + } else if (m_transitionState->status() == EffectTogglableState::Status::Active) { + m_transitionState->deactivate(); + } else if (m_gridState->status() == EffectTogglableState::Status::Inactive) { + m_gridState->activate(); + } } void OverviewEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent) { - if (m_toggleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { + if (m_cycleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { if (keyEvent->type() == QEvent::KeyPress) { - m_state->toggle(); + cycle(); + } + return; + } + if (m_reverseCycleShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { + if (keyEvent->type() == QEvent::KeyPress) { + reverseCycle(); + } + return; + } + if (m_overviewShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { + if (keyEvent->type() == QEvent::KeyPress) { + m_overviewState->toggleAction(); + } + return; + } + if (m_gridShortcut.contains(keyEvent->key() | keyEvent->modifiers())) { + if (keyEvent->type() == QEvent::KeyPress) { + m_gridState->toggleAction(); } return; } QuickSceneEffect::grabbedKeyboardEvent(keyEvent); } +void OverviewEffect::swapDesktops(int from, int to) +{ + QList fromList; + QList toList; + for (auto *w : effects->stackingOrder()) { + if (!w->isNormalWindow() || !w->isOnCurrentActivity()) { + continue; + } + if (w->isOnDesktop(from)) { + fromList << w; + } else if (w->isOnDesktop(to)) { + toList << w; + } + } + for (auto *w : fromList) { + effects->windowToDesktop(w, to); + } + for (auto *w : toList) { + effects->windowToDesktop(w, from); + } +} + } // namespace KWin #include "moc_overvieweffect.cpp" diff --git a/src/plugins/overview/overvieweffect.h b/src/plugins/overview/overvieweffect.h index 5d8e1011e9..31958fa9c1 100644 --- a/src/plugins/overview/overvieweffect.h +++ b/src/plugins/overview/overvieweffect.h @@ -18,9 +18,15 @@ class OverviewEffect : public QuickSceneEffect Q_PROPERTY(int animationDuration READ animationDuration NOTIFY animationDurationChanged) Q_PROPERTY(int layout READ layout NOTIFY layoutChanged) Q_PROPERTY(bool ignoreMinimized READ ignoreMinimized NOTIFY ignoreMinimizedChanged) - Q_PROPERTY(qreal partialActivationFactor READ partialActivationFactor NOTIFY partialActivationFactorChanged) - // More efficient from a property binding pov rather than binding to partialActivationFactor !== 0 - Q_PROPERTY(bool gestureInProgress READ gestureInProgress NOTIFY gestureInProgressChanged) + Q_PROPERTY(bool organizedGrid READ organizedGrid NOTIFY organizedGridChanged) + Q_PROPERTY(qreal overviewPartialActivationFactor READ overviewPartialActivationFactor NOTIFY overviewPartialActivationFactorChanged) + // More efficient from a property binding pov rather than checking if partialActivationFactor is strictly between 0 and 1 + Q_PROPERTY(bool overviewGestureInProgress READ overviewGestureInProgress NOTIFY overviewGestureInProgressChanged) + Q_PROPERTY(qreal transitionPartialActivationFactor READ transitionPartialActivationFactor NOTIFY transitionPartialActivationFactorChanged) + Q_PROPERTY(bool transitionGestureInProgress READ transitionGestureInProgress NOTIFY transitionGestureInProgressChanged) + Q_PROPERTY(qreal gridPartialActivationFactor READ gridPartialActivationFactor NOTIFY gridPartialActivationFactorChanged) + Q_PROPERTY(bool gridGestureInProgress READ gridGestureInProgress NOTIFY gridGestureInProgressChanged) + Q_PROPERTY(QPointF desktopOffset READ desktopOffset NOTIFY desktopOffsetChanged) Q_PROPERTY(QString searchText MEMBER m_searchText NOTIFY searchTextChanged) public: @@ -31,31 +37,38 @@ public: void setLayout(int layout); bool ignoreMinimized() const; + bool organizedGrid() const; int animationDuration() const; void setAnimationDuration(int duration); - qreal partialActivationFactor() const - { - return m_state->partialActivationFactor(); - } - - bool gestureInProgress() const - { - return m_state->inProgress(); - } + qreal overviewPartialActivationFactor() const; + bool overviewGestureInProgress() const; + qreal transitionPartialActivationFactor() const; + bool transitionGestureInProgress() const; + qreal gridPartialActivationFactor() const; + bool gridGestureInProgress() const; + QPointF desktopOffset() const; int requestedEffectChainPosition() const override; bool borderActivated(ElectricBorder border) override; void reconfigure(ReconfigureFlags flags) override; void grabbedKeyboardEvent(QKeyEvent *keyEvent) override; + Q_INVOKABLE void swapDesktops(int from, int to); + Q_SIGNALS: void animationDurationChanged(); void layoutChanged(); - void partialActivationFactorChanged(); - void gestureInProgressChanged(); + void overviewPartialActivationFactorChanged(); + void overviewGestureInProgressChanged(); + void transitionPartialActivationFactorChanged(); + void transitionGestureInProgressChanged(); + void gridPartialActivationFactorChanged(); + void gridGestureInProgressChanged(); void ignoreMinimizedChanged(); + void organizedGridChanged(); + void desktopOffsetChanged(); void searchTextChanged(); public Q_SLOTS: @@ -64,14 +77,22 @@ public Q_SLOTS: private: void realDeactivate(); + void cycle(); + void reverseCycle(); - EffectTogglableState *const m_state; + EffectTogglableState *const m_overviewState; + EffectTogglableState *const m_transitionState; + EffectTogglableState *const m_gridState; EffectTogglableTouchBorder *const m_border; QTimer *m_shutdownTimer; - QList m_toggleShortcut; + QList m_cycleShortcut; + QList m_reverseCycleShortcut; + QList m_overviewShortcut; + QList m_gridShortcut; QList m_borderActivate; QString m_searchText; + QPointF m_desktopOffset; int m_animationDuration = 400; int m_layout = 1; }; diff --git a/src/plugins/overview/qml/DesktopBar.qml b/src/plugins/overview/qml/DesktopBar.qml index 4822c873d7..b70a530cb7 100644 --- a/src/plugins/overview/qml/DesktopBar.qml +++ b/src/plugins/overview/qml/DesktopBar.qml @@ -6,28 +6,31 @@ */ import QtQuick -import Qt5Compat.GraphicalEffects import QtQuick.Controls import QtQuick.Layouts +import Qt5Compat.GraphicalEffects import org.kde.kirigami 2.20 as Kirigami import org.kde.kwin as KWinComponents -import org.kde.kwin.private.effects -import org.kde.plasma.components 3.0 as PC3 -import org.kde.kirigami 2.20 as Kirigami +import org.kde.kwin.private.effects 1.0 +import org.kde.plasma.components as PC3 Item { id: bar - readonly property real desktopHeight: Kirigami.Units.gridUnit * 4 + readonly property real desktopHeight: Kirigami.Units.gridUnit * 5 readonly property real desktopWidth: desktopHeight * targetScreen.geometry.width / targetScreen.geometry.height readonly property real columnHeight: desktopHeight + Kirigami.Units.gridUnit + readonly property real columnWidth: desktopWidth + Kirigami.Units.gridUnit + readonly property int desktopCount: desktopRepeater.count + property bool verticalDesktopBar property QtObject windowModel property alias desktopModel: desktopRepeater.model property QtObject selectedDesktop: null - property WindowHeap heap + property var heap implicitHeight: columnHeight + 2 * Kirigami.Units.smallSpacing + implicitWidth: columnWidth + 2 * Kirigami.Units.smallSpacing Flickable { anchors.fill: parent @@ -39,8 +42,9 @@ Item { clip: true flickableDirection: Flickable.HorizontalFlick - Row { - spacing: Kirigami.Units.gridUnit + Grid { + spacing: Kirigami.Units.largeSpacing + columns: verticalDesktopBar ? 1 : desktopCount + 1 Repeater { id: desktopRepeater @@ -71,7 +75,11 @@ Item { Keys.onRightPressed: nextItemInFocusChain(!LayoutMirroring.enabled).forceActiveFocus(Qt.TabFocusReason); function activate() { - KWinComponents.Workspace.currentDesktop = delegate.desktop; + if (KWinComponents.Workspace.currentDesktop === delegate.desktop) { + effect.deactivate() + } else { + KWinComponents.Workspace.currentDesktop = delegate.desktop; + } } function remove() { @@ -92,40 +100,48 @@ Item { width: bar.desktopWidth height: bar.desktopHeight + scale: thumbnailHover.hovered ? 1.03 : 1 + + Behavior on scale { + NumberAnimation { + duration: Kirigami.Units.Kirigami.Units.shortDuration + } + } + + HoverHandler { + id: thumbnailHover + } + DesktopView { id: thumbnail width: targetScreen.geometry.width height: targetScreen.geometry.height - visible: false windowModel: bar.windowModel desktop: delegate.desktop scale: bar.desktopHeight / targetScreen.geometry.height transformOrigin: Item.TopLeft - // Disable the item layer while being scaled. - layer.enabled: true - layer.textureSize: Qt.size(bar.desktopWidth, bar.desktopHeight) - } - OpacityMask { - anchors.fill: parent - cached: true - source: thumbnail - maskSource: Rectangle { - width: bar.desktopWidth - height: bar.desktopHeight - radius: 3 + layer.textureSize: Qt.size(bar.desktopWidth, bar.desktopHeight) + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + anchors.centerIn: parent + width: thumbnail.width + height: thumbnail.height + // Using 5% of width since that's constant even under scaling: + radius: width / 20 + } } } Rectangle { - readonly property bool active: delegate.activeFocus || dropArea.containsDrag || mouseArea.containsPress || bar.selectedDesktop === delegate.desktop + readonly property bool active: (delegate.activeFocus || dropArea.containsDrag || mouseArea.containsPress || bar.selectedDesktop === delegate.desktop) anchors.fill: parent - anchors.margins: -border.width - radius: 3 + radius: width / 20 color: "transparent" - border.width: 2 + border.width: active ? 2 : 1 border.color: active ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor opacity: dropArea.containsDrag || !active ? 0.5 : 1.0 } @@ -145,18 +161,11 @@ Item { break; } } - onDoubleClicked: { - if (mouse.button == Qt.LeftButton) { - mouse.accepted = true; - delegate.activate(); - effect.deactivate(); - } - } } Loader { LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft - active: !heap.dragActive && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && desktopRepeater.count > 1 + active: !bar.heap.dragActive && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && desktopCount > 1 anchors.right: parent.right anchors.top: parent.top sourceComponent: PC3.Button { @@ -281,8 +290,7 @@ Item { drag.accepted = desktopModel.rowCount() < 20 } onDropped: { - const desktop = desktopModel.create(desktopModel.rowCount()); - drag.source.desktops = [desktop]; + drag.source.desktops = [desktopModel.create(desktopModel.rowCount())]; } } } diff --git a/src/plugins/overview/qml/main.qml b/src/plugins/overview/qml/main.qml index d05ce1cba2..ff844636cb 100644 --- a/src/plugins/overview/qml/main.qml +++ b/src/plugins/overview/qml/main.qml @@ -1,19 +1,21 @@ /* SPDX-FileCopyrightText: 2021 Vlad Zahorodnii SPDX-FileCopyrightText: 2022 ivan tkachenko + SPDX-FileCopyrightText: 2023 Niccolò Venerandi SPDX-License-Identifier: GPL-2.0-or-later */ import QtQuick -import QtQuick.Controls import Qt5Compat.GraphicalEffects +import QtQuick.Layouts import org.kde.kirigami 2.20 as Kirigami import org.kde.kwin as KWinComponents import org.kde.kwin.private.effects import org.kde.milou as Milou -import org.kde.kirigami 2.20 as Kirigami +import org.kde.plasma.components as PC3 import org.kde.plasma.extras as PlasmaExtras +import org.kde.kcmutils as KCM FocusScope { id: container @@ -29,135 +31,176 @@ FocusScope { property bool animationEnabled: false property bool organized: false - property alias currentHeap: heapView.currentItem + property bool verticalDesktopBar: KWinComponents.Workspace.desktopGridHeight >= bar.desktopCount && KWinComponents.Workspace.desktopGridHeight != 1 + property bool anyDesktopBar: verticalDesktopBar || KWinComponents.Workspace.desktopGridHeight == 1 + + // The values of overviewVal and gridVal might not be 0 on startup, + // but we always want to animate from 0 to those values. So, we initially + // always set them to 0 and bind their full values when the effect starts. + // See start() + property real overviewVal: 0 + property real gridVal: 0 + + Behavior on overviewVal { + NumberAnimation { + duration: Kirigami.Units.shortDuration + } + } + + Behavior on gridVal { + NumberAnimation { + duration: Kirigami.Units.shortDuration + } + } function start() { animationEnabled = true; organized = true; + + overviewVal = Qt.binding(() => effect.overviewGestureInProgress ? effect.overviewPartialActivationFactor : + effect.transitionGestureInProgress ? 1 - effect.transitionPartialActivationFactor : + effect.overviewPartialActivationFactor == 1 && effect.transitionPartialActivationFactor == 0) + gridVal = Qt.binding(() => effect.transitionGestureInProgress ? effect.transitionPartialActivationFactor : + effect.gridGestureInProgress ? effect.gridPartialActivationFactor : + effect.transitionPartialActivationFactor == 1 && effect.gridPartialActivationFactor == 1) } function stop() { organized = false; } - Keys.onEscapePressed: effect.deactivate(); - - Keys.forwardTo: searchField - - Keys.onEnterPressed: { - currentHeap.forceActiveFocus(); - if (currentHeap.count === 1) { - currentHeap.activateCurrentClient(); - } + function switchTo(desktop) { + KWinComponents.Workspace.currentDesktop = desktop; + effect.deactivate(); } - Keys.onLeftPressed: { - let view = effect.getView(Qt.LeftEdge) - if (view) { - effect.activateView(view) - } - } - Keys.onRightPressed: { - let view = effect.getView(Qt.RightEdge) - if (view) { - effect.activateView(view) - } - } - Keys.onUpPressed: { - let view = effect.getView(Qt.TopEdge) - if (view) { - effect.activateView(view) - } - } - Keys.onDownPressed: { - let view = effect.getView(Qt.BottomEdge) - if (view) { - effect.activateView(view) + function selectNext(direction) { + if (effect.searchText !== "") return false; + let currentIndex = 0 + for (let i = 0; i < allDesktopHeaps.count; i++) { + if (allDesktopHeaps.itemAt(i).current) { + currentIndex = i; + break; + } + } + let x = currentIndex % container.columns; + let y = Math.floor(currentIndex / container.columns); + + // the direction we move in is the opposite of the window to select + // i.e pressing left should select the rightmost window on the desktop + // to the left + let invertedDirection; + switch(direction) { + case WindowHeap.Direction.Up: + y--; + invertedDirection = WindowHeap.Direction.Down; + break; + case WindowHeap.Direction.Down: + y++ + invertedDirection = WindowHeap.Direction.Up; + break; + case WindowHeap.Direction.Left: + x--; + invertedDirection = WindowHeap.Direction.Right; + break; + case WindowHeap.Direction.Right: + x++; + invertedDirection = WindowHeap.Direction.Left; + break; + } + + if (x < 0 || x >= container.columns) { + return false; + } + if (y < 0 || y >= container.rows) { + return false; + } + let newIndex = y * container.columns + x; + + KWinComponents.Workspace.currentDesktop = allDesktopHeaps.itemAt(newIndex).desktop + allDesktopHeaps.itemAt(newIndex).nestedHeap.focus = true + allDesktopHeaps.itemAt(newIndex).selectLastItem(invertedDirection); + return true; + } + + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape) { + if (effect.searchText !== "") { + effect.searchText = "" + } else { + effect.deactivate(); + } + } else if (event.key === Qt.Key_Plus || event.key === Qt.Key_Equal) { + desktopModel.create(desktopModel.rowCount()); + } else if (event.key === Qt.Key_Minus) { + desktopModel.remove(desktopModel.rowCount() - 1); + } else if (event.key >= Qt.Key_F1 && event.key <= Qt.Key_F12) { + const desktopId = event.key - Qt.Key_F1; + if (desktopId < allDesktopHeaps.count) { + switchTo(allDesktopHeaps.itemAt(desktopId).desktop); + } + } else if (event.key >= Qt.Key_0 && event.key <= Qt.Key_9) { + const desktopId = event.key === Qt.Key_0 ? 9 : (event.key - Qt.Key_1); + if (desktopId < allDesktopHeaps.count) { + switchTo(allDesktopHeaps.itemAt(desktopId).desktop); + } + } else if (event.key === Qt.Key_Up) { + event.accepted = selectNext(WindowHeap.Direction.Up); + if (!event.accepted) { + let view = effect.getView(Qt.TopEdge) + if (view) { + effect.activateView(view) + } + } + } else if (event.key === Qt.Key_Down) { + event.accepted = selectNext(WindowHeap.Direction.Down); + if (!event.accepted) { + let view = effect.getView(Qt.BottomEdge) + if (view) { + effect.activateView(view) + } + } + } else if (event.key === Qt.Key_Left) { + event.accepted = selectNext(WindowHeap.Direction.Left); + if (!event.accepted) { + let view = effect.getView(Qt.LeftEdge) + if (view) { + effect.activateView(view) + } + } + } else if (event.key === Qt.Key_Right) { + event.accepted = selectNext(WindowHeap.Direction.Right); + if (!event.accepted) { + let view = effect.getView(Qt.RightEdge) + if (view) { + effect.activateView(view) + } + } + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Space) { + for (let i = 0; i < allDesktopHeaps.count; i++) { + if (allDesktopHeaps.itemAt(i).current) { + switchTo(allDesktopHeaps.itemAt(i).desktop) + break; + } + } } } + Keys.priority: Keys.AfterItem KWinComponents.DesktopBackground { id: backgroundItem activity: KWinComponents.Workspace.currentActivity desktop: KWinComponents.Workspace.currentDesktop outputName: targetScreen.name - property real blurRadius: 0 layer.enabled: true - layer.effect: FastBlur { - radius: backgroundItem.blurRadius - } - } - - state: { - if (effect.gestureInProgress) { - return "partial"; - } else if (organized) { - return "active"; - } else { - return "initial"; - } - } - - states: [ - State { - name: "initial" - PropertyChanges { - target: underlay - opacity: 0 - } - PropertyChanges { - target: topBar - opacity: 0 - } - PropertyChanges { - target: backgroundItem - blurRadius: 0 - } - }, - State { - name: "partial" - PropertyChanges { - target: underlay - opacity: 0.75 * effect.partialActivationFactor - } - PropertyChanges { - target: topBar - opacity: effect.partialActivationFactor - } - PropertyChanges { - target: backgroundItem - blurRadius: 64 * effect.partialActivationFactor - } - }, - State { - name: "active" - PropertyChanges { - target: underlay - opacity: 0.75 - } - PropertyChanges { - target: topBar - opacity: 1 - } - PropertyChanges { - target: backgroundItem - blurRadius: 64 - } - } - ] - transitions: Transition { - to: "initial, active" - NumberAnimation { - duration: effect.animationDuration - properties: "opacity, blurRadius" - easing.type: Easing.OutCubic - } + layer.effect: FastBlur {radius: 64} } Rectangle { id: underlay anchors.fill: parent + opacity: 0.7 color: Kirigami.Theme.backgroundColor TapHandler { @@ -165,112 +208,355 @@ FocusScope { } } - Column { - anchors.fill: parent + Item { + id: desktopBar + visible: container.anyDesktopBar - Item { - id: topBar - width: parent.width - height: searchBar.height + desktopBar.height + // (overviewVal, gridVal) represents the state of the overview + // in a 2D coordinate plane. Math.atan2 returns the angle between + // the x axis and that point. By using this to set the opacity, + // we can have an opaque desktopBar when the point moves from + // the origin to the "overviewVal" direction, which has angle pi/2, + // and a transparent desktopBar when the point moves from + // the origin to the "gridVal" direction, which has angle 0, + // whilst still animating when the point moves from overviewVal to + // gridVal too. + opacity: Math.atan2(overviewVal, gridVal) / Math.PI * 2 - Rectangle { - id: desktopBar - width: parent.width - implicitHeight: bar.implicitHeight + 2 * Kirigami.Units.smallSpacing - color: container.lightBackground ? Qt.rgba(Kirigami.Theme.backgroundColor.r, - Kirigami.Theme.backgroundColor.g, - Kirigami.Theme.backgroundColor.b, 0.75) - : Qt.rgba(0, 0, 0, 0.25) + anchors.top: parent.top + anchors.left: parent.left + anchors.right: container.verticalDesktopBar ? undefined : parent.right + anchors.bottom: container.verticalDesktopBar ? parent.bottom : undefined + height: bar.implicitHeight + 2 * Kirigami.Units.smallSpacing + width: bar.implicitWidth + 2 * Kirigami.Units.smallSpacing - DesktopBar { - id: bar - anchors.fill: parent - windowModel: stackModel - desktopModel: desktopModel - selectedDesktop: KWinComponents.Workspace.currentDesktop - heap: currentHeap - } + DesktopBar { + id: bar + anchors.fill: parent + windowModel: stackModel + desktopModel: desktopModel + verticalDesktopBar: container.verticalDesktopBar + selectedDesktop: KWinComponents.Workspace.currentDesktop + heap: allDesktopHeaps.currentHeap + } + } + + Item { + id: topBar + opacity: desktopBar.opacity + anchors.left: container.verticalDesktopBar ? desktopBar.right : parent.left + anchors.right: parent.right + anchors.top: container.verticalDesktopBar || !container.anyDesktopBar ? parent.top : desktopBar.bottom + anchors.topMargin: Kirigami.Units.largeSpacing + height: searchField.height + 1 * Kirigami.Units.largeSpacing + + PlasmaExtras.SearchField { + id: searchField + anchors.centerIn: parent + width: Math.min(parent.width, 20 * Kirigami.Units.gridUnit) + focus: enabled + readOnly: gridVal == 1 + onReadOnlyChanged: { + text = "" + effect.searchText = "" + effect.searchTextChanged() } - - Item { - id: searchBar - anchors.top: desktopBar.bottom - width: parent.width - height: searchField.height + 2 * Kirigami.Units.gridUnit - - PlasmaExtras.SearchField { - id: searchField - anchors.centerIn: parent - width: Math.min(parent.width, 20 * Kirigami.Units.gridUnit) - focus: true - Keys.priority: Keys.BeforeItem - Keys.forwardTo: text && currentHeap.count === 0 ? searchResults : currentHeap - text: effect.searchText - onTextEdited: { - effect.searchText = text; - currentHeap.resetSelected(); - currentHeap.selectNextItem(WindowHeap.Direction.Down); - searchField.focus = true; - } - } + Keys.priority: Keys.BeforeItem + Keys.forwardTo: text && allDesktopHeaps.currentHeap.count === 0 ? searchResults : allDesktopHeaps.currentHeap + text: effect.searchText + onTextEdited: { + effect.searchText = text; + allDesktopHeaps.currentHeap.resetSelected(); + allDesktopHeaps.currentHeap.selectNextItem(WindowHeap.Direction.Down); + searchField.focus = true; } } + } - Item { - width: parent.width - height: parent.height - topBar.height + property var currentGeometry: targetScreen.geometry - PlasmaExtras.PlaceholderMessage { - id: placeholderMessage - anchors.top: parent.top - anchors.horizontalCenter: parent.horizontalCenter - visible: container.organized && effect.searchText.length > 0 && currentHeap.count === 0 - text: i18nd("kwin", "No matching windows") + // These are the minimum position of maximum size of the desktop preview in the overview + property int minX: Kirigami.Units.largeSpacing + (container.verticalDesktopBar ? desktopBar.width : 0) + property int minY: Kirigami.Units.largeSpacing + topBar.height + (container.verticalDesktopBar || !container.anyDesktopBar ? 0 : desktopBar.height) + property int maxWidth: currentGeometry.width - minX - Kirigami.Units.gridUnit * 2 + property int maxHeight: currentGeometry.height - minY - Kirigami.Units.gridUnit * 2 + + property int desktops: Math.max(bar.desktopCount, 2) + property int columns: Math.ceil(desktops / rows) + property int rows: KWinComponents.Workspace.desktopGridHeight + + // The desktop might shuffle around as soon as it's + // created since the rows/columns are updated after + // the desktop is added. We don't want to see that. + property bool desktopJustCreated: false + onDesktopsChanged: { + desktopJustCreated = true + startTimer.running = true + } + + Timer { + id: startTimer + interval: effect.animationDuration + running: false + onTriggered: desktopJustCreated = false + } + + Item { + id: desktopGrid + anchors.fill: parent + property var dndManagerStore: ({}) + + ColumnLayout { + x: Math.round(parent.width / 2) + Math.round(parent.width / 8) + width: Math.round(parent.width / 2) - Math.round(parent.width / 8) * 2 + anchors.verticalCenter: parent.verticalCenter + visible: bar.desktopCount === 1 + opacity: gridVal + spacing: Kirigami.Units.largeSpacing + + Kirigami.PlaceholderMessage { + text: i18ndc("kwin", "@info:placeholder", "No other Virtual Desktops to show") + icon.name: "virtual-desktops-symbolic" } - StackView { - id: heapView + PC3.Button { + Layout.alignment: Qt.AlignHCenter + text: i18ndc("kwin", "@action:button", "Add Virtual Desktop") + icon.name: "list-add-symbolic" + onClicked: desktopModel.create(desktopModel.rowCount()) + } + + PC3.Button { + Layout.alignment: Qt.AlignHCenter + text: i18ndc("kwin", "@action:button", "Configure Virtual Desktops…") + icon.name: "preferences-virtual-desktops" + onClicked: { + KCM.KCMLauncher.openSystemSettings("kcm_kwin_virtualdesktops") + effect.deactivate(); + } + } + + } + + + Repeater { + id: allDesktopHeaps + model: desktopModel + + property Item currentHeap + property Item currentBackgroundItem + + Kirigami.ShadowedRectangle { + id: mainBackground + color: Kirigami.Theme.highlightColor + visible: gridVal > 0 || nearCurrent anchors.fill: parent + property bool shouldBeVisibleInOverview: !(container.organized && effect.searchText.length > 0 && current) || heap.count !== 0 + opacity: 1 - overviewVal * (shouldBeVisibleInOverview ? 0 : 1) - function switchTo(desktop) { - container.animationEnabled = false; - heapView.replace(heapTemplate, { desktop: desktop }); - currentItem.layout.forceLayout(); - container.animationEnabled = true; + function selectLastItem(direction) { + heap.selectLastItem(direction); } - Component.onCompleted: { - push(heapTemplate, { desktop: KWinComponents.Workspace.currentDesktop }); - } - } - Connections { - target: KWinComponents.Workspace - function onCurrentDesktopChanged() { - heapView.switchTo(KWinComponents.Workspace.currentDesktop); - } - } + required property QtObject desktop + required property int index + readonly property bool current: KWinComponents.Workspace.currentDesktop === desktop + readonly property bool nearCurrent: Math.abs(deltaColumn) <= 1 && Math.abs(deltaRow) <= 1 + readonly property var nestedHeap: heap - Component { - id: heapTemplate + z: dragActive ? 1 : 0 + readonly property bool dragActive: heap.dragActive || dragHandler.active + + shadow { + size: Kirigami.Units.gridUnit * 2 + color: Qt.rgba(0, 0, 0, 0.3) + yOffset: 3 + } + radius: Kirigami.Units.largeSpacing * 2 * (overviewVal + gridVal * 2) + + property int gridSize: Math.max(rows, columns) + property real row: (index - column) / columns + property real column: index % columns + // deltaX and deltaY are used to move all the desktops together to 1:1 animate the + // switching between different desktops + property real deltaX: (!current ? effect.desktopOffset.x : + column == 0 ? Math.max(0, effect.desktopOffset.x) : + column == columns - 1 ? Math.min(0, effect.desktopOffset.x) : + effect.desktopOffset.x) + property real deltaY: (!current ? effect.desktopOffset.y : + row == 0 ? Math.max(0, effect.desktopOffset.y) : + row == rows - 1 ? Math.min(0, effect.desktopOffset.y) : + effect.desktopOffset.y) + // deltaColumn and deltaRows are the difference between the column/row of this desktop + // compared to the column/row of the active one + property real deltaColumn: column - allDesktopHeaps.currentBackgroundItem.column - deltaX + property real deltaRow: row - allDesktopHeaps.currentBackgroundItem.row - deltaY + + Behavior on deltaColumn { + enabled: overviewVal > 0 && !container.desktopJustCreated + NumberAnimation { + duration: effect.animationDuration + easing.type: Easing.OutCubic + } + } + Behavior on deltaRow { + enabled: overviewVal > 0 && !container.desktopJustCreated + NumberAnimation { + duration: effect.animationDuration + easing.type: Easing.OutCubic + } + } + + // Note that transforms should be read from the last one to the first one + transform: [ + // Scales down further, still in grid, to have some gaps between + // the desktops. + Scale { + origin.x: width / 2 + origin.y: height / 2 + xScale: 1 - 0.02 * gridVal + yScale: xScale + }, + // Scales down the desktops so that they do not overlap in the grid + Scale { + id: gridScale + xScale: 1 + (1 / gridSize - 1) * gridVal + yScale: xScale + }, + // Further little translation in the grids to align the elements + // to the center of their cell + Translate{ + x: (gridSize / columns - 1) * (width / gridSize) / 2 * gridVal + y: (gridSize / rows - 1) * (height / gridSize) / 2 * gridVal + }, + // When in desktop grid, translates the desktop so that the whole + // grid fits in the view + Translate { + x: column * (width / columns) * gridVal + y: row * (height / rows) * gridVal + }, + // Scales down the preview slighly when in Overview mode + Scale { + origin.x: width / 2 + origin.y: height / 2 + property real scale: Math.min(maxWidth / width, maxHeight / height) + xScale: 1 + (scale - 1) * overviewVal + yScale:1 + (scale - 1) * overviewVal + }, + // Initially places transition desktops in a grid around the current one, + // and moves them slighly to avoid overlapping the UI + Translate { + x: minX * 0.5 * overviewVal + deltaColumn * width * (1 - gridVal) + y: minY * 0.5 * overviewVal + deltaRow * height * (1 - gridVal) + } + ] + + KWinComponents.DesktopBackground { + id: desktopElement + anchors.fill: parent + anchors.margins: gridVal !== 0 ? Math.round(mainBackground.current * gridVal * (1.5 / gridScale.xScale)) : 0 + activity: KWinComponents.Workspace.currentActivity + desktop: KWinComponents.Workspace.currentDesktop + outputName: targetScreen.name + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + anchors.centerIn: parent + width: desktopElement.width + height: desktopElement.height + radius: mainBackground.radius + } + } + } + + DropArea { + anchors.fill: parent + onEntered: (drag) => { + drag.accepted = true; + } + onDropped: (drop) => { + drop.accepted = true; + if (drag.source instanceof Kirigami.ShadowedRectangle) { + // dragging a desktop as a whole + if (drag.source === mainBackground) { + drop.action = Qt.IgnoreAction; + return; + } + effect.swapDesktops(drag.source.desktop.x11DesktopNumber, desktop.x11DesktopNumber); + } else { + // dragging a KWin::Window + if (drag.source.desktops.length === 0 || drag.source.desktops.indexOf(mainBackground.desktop) !== -1) { + drop.action = Qt.IgnoreAction; + return; + } + drag.source.desktops = [mainBackground.desktop]; + } + } + } + Connections { + target: effect + function onItemDroppedOutOfScreen(globalPos, item, screen) { + if (screen !== targetScreen) { + return; + } + const pos = screen.mapFromGlobal(globalPos); + if (!mainBackground.contains(mainBackground.mapFromItem(null, pos.x, pos.y))) { + return; + } + item.client.desktops = [mainBackground.desktop]; + } + } + DragHandler { + id: dragHandler + target: heap + enabled: gridVal !== 0 + grabPermissions: PointerHandler.ApprovesTakeOverByHandlersOfSameType + onActiveChanged: { + if (!active) { + heap.Drag.drop(); + Qt.callLater(heap.resetPosition) + } + } + } + + MouseArea { + anchors.fill: heap + acceptedButtons: Qt.NoButton + cursorShape: dragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor + } WindowHeap { id: heap + width: parent.width + height: parent.height + x: 0 + y: 0 - required property QtObject desktop + function resetPosition() { + x = 0; + y = 0; + } + z: 9999 + Drag.active: dragHandler.active + Drag.proposedAction: Qt.MoveAction + Drag.supportedActions: Qt.MoveAction + Drag.source: mainBackground + Drag.hotSpot: Qt.point(width * 0.5, height * 0.5) - visible: !(container.organized && effect.searchText.length > 0) || heap.count !== 0 layout.mode: effect.layout - focus: true - padding: Kirigami.Units.gridUnit + focus: current + padding: Kirigami.Units.largeSpacing animationDuration: effect.animationDuration - animationEnabled: container.animationEnabled + animationEnabled: container.animationEnabled && (gridVal !== 0 || mainBackground.current) organized: container.organized + dndManagerStore: desktopGrid.dndManagerStore Keys.priority: Keys.AfterItem - Keys.forwardTo: searchResults + Keys.forwardTo: [searchResults, searchField] model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity - desktop: heap.desktop + desktop: mainBackground.desktop screenName: targetScreen.name windowModel: stackModel filter: effect.searchText @@ -283,30 +569,93 @@ FocusScope { delegate: WindowHeapDelegate { windowHeap: heap + // This is preferable over using gestureInProgress values since gridVal and + // overviewVal are animated even after the gesture ends, and since the partial + // activation factor follows those two values, this results in a more + // fluent animation. + gestureInProgress: !Number.isInteger(gridVal) || !Number.isInteger(overviewVal) + + partialActivationFactor: container.overviewVal + container.gridVal * effect.organizedGrid + targetScale: { + if (!container.anyDesktopBar) return targetScale; + if (overviewVal != 1) return targetScale; + let coordinate = container.verticalDesktopBar ? 'x' : 'y' if (!activeDragHandler.active) { return targetScale; // leave it alone, so it won't affect transitions before they start } - var localPressPosition = activeDragHandler.centroid.scenePressPosition.y - heap.layout.Kirigami.ScenePosition.y; + var localPressPosition = activeDragHandler.centroid.scenePressPosition[coordinate] - heap.layout.Kirigami.ScenePosition[coordinate]; if (localPressPosition === 0) { return 0.1; } else { - var localPosition = activeDragHandler.centroid.scenePosition.y - heap.layout.Kirigami.ScenePosition.y; + var localPosition = activeDragHandler.centroid.scenePosition[coordinate] - heap.layout.Kirigami.ScenePosition[coordinate]; return Math.max(0.1, Math.min(localPosition / localPressPosition, 1)); } } opacity: 1 - downGestureProgress onDownGestureTriggered: window.closeWindow() - TapHandler { acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen - acceptedButtons: Qt.MiddleButton - onTapped: window.closeWindow() + acceptedButtons: Qt.MiddleButton | Qt.RightButton + onTapped: (eventPoint, button) => { + if (button === Qt.MiddleButton) { + window.closeWindow(); + } else if (button === Qt.RightButton) { + if (window.desktops.length > 0) { + window.desktops = []; + } else { + window.desktops = [desktopView.desktop]; + } + } + } } } - onActivated: effect.deactivate(); + onActivated: effect.deactivate() } + TapHandler { + acceptedButtons: Qt.LeftButton + onTapped: { + KWinComponents.Workspace.currentDesktop = mainBackground.desktop; + container.effect.deactivate(); + } + } + onCurrentChanged: { + if (current) { + allDesktopHeaps.currentHeap = heap; + allDesktopHeaps.currentBackgroundItem = mainBackground; + } + } + Component.onCompleted: { + if (current) { + allDesktopHeaps.currentHeap = heap; + allDesktopHeaps.currentBackgroundItem = mainBackground; + } + startTimer.running = true + } + } + + } + + } + + Column { + anchors.left: container.verticalDesktopBar ? desktopBar.right : parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.top: topBar.bottom + + Item { + width: parent.width + height: parent.height - topBar.height + visible: container.organized && effect.searchText.length > 0 && allDesktopHeaps.currentHeap.count === 0 + opacity: overviewVal + + PlasmaExtras.PlaceholderMessage { + id: placeholderMessage + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + text: i18ndc("kwin", "@info:placeholder", "No matching windows") } Milou.ResultsView { @@ -314,9 +663,8 @@ FocusScope { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width / 2 - height: parent.height - placeholderMessage.height - Kirigami.Units.gridUnit + height: parent.height - placeholderMessage.height - Kirigami.Units.largeSpacing queryString: effect.searchText - visible: container.organized && effect.searchText.length > 0 && currentHeap.count === 0 onActivated: { effect.deactivate(); @@ -342,14 +690,7 @@ FocusScope { z: model.window.stackingOrder width: model.window.width height: model.window.height - opacity: container.effect.gestureInProgress - ? 1 - container.effect.partialActivationFactor - : (model.window.hidden || container.organized) ? 0 : 1 - - Behavior on opacity { - enabled: !container.effect.gestureInProgress - NumberAnimation { duration: effect.animationDuration; easing.type: Easing.OutCubic } - } + opacity: 1 - (gridVal + overviewVal) } } @@ -361,5 +702,12 @@ FocusScope { id: desktopModel } - Component.onCompleted: start(); + Component.onCompleted: { + // The following line unbinds the verticalDesktopBar, meaning that it + // won't react to changes in number of desktops or rows. This is beacuse we + // don't want the desktop bar changing screenside whilst the user is + // interacting with it, e.g. by adding desktops + container.verticalDesktopBar = container.verticalDesktopBar + start(); + } } diff --git a/src/plugins/private/qml/WindowHeapDelegate.qml b/src/plugins/private/qml/WindowHeapDelegate.qml index 0c57626a69..6b4227b481 100644 --- a/src/plugins/private/qml/WindowHeapDelegate.qml +++ b/src/plugins/private/qml/WindowHeapDelegate.qml @@ -7,10 +7,12 @@ import QtQuick import QtQuick.Window +import Qt5Compat.GraphicalEffects import org.kde.kirigami 2.20 as Kirigami import org.kde.kwin as KWinComponents import org.kde.kwin.private.effects import org.kde.plasma.components 3.0 as PC3 +import org.kde.plasma.workspace.components 2.0 as WorkspaceComponents import org.kde.ksvg 1.0 as KSvg Item { @@ -21,6 +23,8 @@ Item { required property Item windowHeap readonly property bool selected: windowHeap.selectedIndex === index + property real partialActivationFactor: effect.partialActivationFactor + property bool gestureInProgress: effect.gestureInProgress // no desktops is a special value which means "All Desktops" readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1 @@ -59,10 +63,10 @@ Item { property string substate: "normal" state: { - if (effect.gestureInProgress) { + if (thumb.gestureInProgress) { return "partial"; } - if (windowHeap.effectiveOrganized) { + if (thumb.partialActivationFactor > 0.5 && cell.isReady) { return activeHidden ? "active-hidden" : `active-${substate}`; } return initialHidden ? "initial-hidden" : "initial"; @@ -163,10 +167,11 @@ Item { anchors.bottomMargin: -Math.round(height / 4) visible: !thumb.activeHidden && !activeDragHandler.active - PC3.Label { + + WorkspaceComponents.ShadowedLabel { id: caption visible: thumb.windowTitleVisible - width: Math.min(implicitWidth, thumbSource.width) + width: Math.min(implicitWidth, thumb.width) anchors.top: parent.bottom anchors.horizontalCenter: parent.horizontalCenter elide: Text.ElideRight @@ -187,6 +192,7 @@ Item { naturalHeight: thumb.window.height persistentKey: thumb.window.internalId bottomMargin: icon.height / 4 + (thumb.windowTitleVisible ? caption.height : 0) + property bool isReady: width !== 0 && height !== 0 } states: [ @@ -219,13 +225,13 @@ Item { name: "partial" PropertyChanges { target: thumb - x: (thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0)) * (1 - effect.partialActivationFactor) + cell.x * effect.partialActivationFactor - y: (thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0)) * (1 - effect.partialActivationFactor) + cell.y * effect.partialActivationFactor - width: thumb.window.width * (1 - effect.partialActivationFactor) + cell.width * effect.partialActivationFactor - height: thumb.window.height * (1 - effect.partialActivationFactor) + cell.height * effect.partialActivationFactor + x: (thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0)) * (1 - thumb.partialActivationFactor) + cell.x * thumb.partialActivationFactor + y: (thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0)) * (1 - thumb.partialActivationFactor) + cell.y * thumb.partialActivationFactor + width: thumb.window.width * (1 - thumb.partialActivationFactor) + cell.width * thumb.partialActivationFactor + height: thumb.window.height * (1 - thumb.partialActivationFactor) + cell.height * thumb.partialActivationFactor opacity: thumb.initialHidden - ? (thumb.activeHidden ? 0 : effect.partialActivationFactor) - : (thumb.activeHidden ? 1 - effect.partialActivationFactor : 1) + ? (thumb.activeHidden ? 0 : thumb.partialActivationFactor) + : (thumb.activeHidden ? 1 - thumb.partialActivationFactor : 1) } PropertyChanges { target: thumbSource @@ -236,11 +242,11 @@ Item { } PropertyChanges { target: icon - opacity: effect.partialActivationFactor + opacity: thumb.partialActivationFactor } PropertyChanges { target: closeButton - opacity: effect.partialActivationFactor + opacity: thumb.partialActivationFactor } }, State { @@ -289,8 +295,8 @@ Item { target: thumbSource x: 0 y: 0 - width: cell.width - height: cell.height + width: thumb.width + height: thumb.height } }, State { @@ -298,8 +304,8 @@ Item { extend: "active" PropertyChanges { target: thumbSource - width: cell.width - height: cell.height + width: thumb.width + height: thumb.height } }, State { @@ -311,8 +317,8 @@ Item { thumb.activeDragHandler.centroid.position.x y: -thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale + thumb.activeDragHandler.centroid.position.y - width: cell.width * thumb.targetScale - height: cell.height * thumb.targetScale + width: thumb.width * thumb.targetScale + height: thumb.height * thumb.targetScale } }, State { diff --git a/src/plugins/windowview/qml/main.qml b/src/plugins/windowview/qml/main.qml index 084a153fcb..d1ec985ea9 100644 --- a/src/plugins/windowview/qml/main.qml +++ b/src/plugins/windowview/qml/main.qml @@ -201,6 +201,7 @@ Item { } delegate: WindowHeapDelegate { windowHeap: heap + partialActivationFactor: container.organized ? 1 : 0 opacity: 1 - downGestureProgress onDownGestureTriggered: window.closeWindow() diff --git a/src/plugins/windowview/windowvieweffect.cpp b/src/plugins/windowview/windowvieweffect.cpp index c907b823b3..d7b9637f05 100644 --- a/src/plugins/windowview/windowvieweffect.cpp +++ b/src/plugins/windowview/windowvieweffect.cpp @@ -125,8 +125,6 @@ WindowViewEffect::WindowViewEffect() } } }; - effects->registerTouchpadSwipeShortcut(SwipeDirection::Down, 4, m_realtimeToggleAction, gestureCallback); - effects->registerTouchscreenSwipeShortcut(SwipeDirection::Down, 3, m_realtimeToggleAction, gestureCallback); reconfigure(ReconfigureAll); }