From 3fba9798e47009c6bcd90ec57469a0cf43e6d470 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Wed, 6 May 2026 11:44:54 +0200 Subject: [PATCH] Add snap layouts shell setting Expose snapLayoutsEnabled through MobileShellSettings and store it\nin plasmamobilerc.\n\nUse the setting in the Shift snap assist effect eligibility logic\nand add a Convergence KCM switch to control it.\n\nKeep the switch disabled when convergence is off, gaming mode is on,\nor dynamic tiling is enabled so window-placement ownership stays\nconsistent. --- .../mobileshellsettings.cpp | 14 ++++++++++++++ .../shellsettingsplugin/mobileshellsettings.h | 12 ++++++++++++ kcms/mobileshell/ui/main.qml | 19 ++++++++++++++++++- .../shift-snap-assist/contents/ui/main.qml | 7 +++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/components/shellsettingsplugin/mobileshellsettings.cpp b/components/shellsettingsplugin/mobileshellsettings.cpp index d1ed6e45..a26157b5 100644 --- a/components/shellsettingsplugin/mobileshellsettings.cpp +++ b/components/shellsettingsplugin/mobileshellsettings.cpp @@ -47,6 +47,7 @@ MobileShellSettings::MobileShellSettings(QObject *parent) Q_EMIT gamingModeEnabledChanged(); Q_EMIT gamingDismissHintEnabledChanged(); Q_EMIT dynamicTilingEnabledChanged(); + Q_EMIT snapLayoutsEnabledChanged(); Q_EMIT allowLogoutChanged(); } if (group.name() == LOCKSCREEN_CONFIG_GROUP) { @@ -290,6 +291,19 @@ void MobileShellSettings::setDynamicTilingEnabled(bool enabled) m_config->sync(); } +bool MobileShellSettings::snapLayoutsEnabled() const +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + return group.readEntry("snapLayoutsEnabled", true); +} + +void MobileShellSettings::setSnapLayoutsEnabled(bool enabled) +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + group.writeEntry("snapLayoutsEnabled", enabled, KConfigGroup::Notify); + m_config->sync(); +} + void MobileShellSettings::updateNavigationBarsInPlasma() { // Do not update panels when not in Plasma Mobile diff --git a/components/shellsettingsplugin/mobileshellsettings.h b/components/shellsettingsplugin/mobileshellsettings.h index 0e5d68a2..fe66e0ad 100644 --- a/components/shellsettingsplugin/mobileshellsettings.h +++ b/components/shellsettingsplugin/mobileshellsettings.h @@ -60,6 +60,9 @@ class MobileShellSettings : public QObject // When false, KWin's native quick-tile behaviour is used unmodified. Q_PROPERTY(bool dynamicTilingEnabled READ dynamicTilingEnabled WRITE setDynamicTilingEnabled NOTIFY dynamicTilingEnabledChanged) + // Snap layout picker — only meaningful in convergence mode when dynamic tiling is off. + Q_PROPERTY(bool snapLayoutsEnabled READ snapLayoutsEnabled WRITE setSnapLayoutsEnabled NOTIFY snapLayoutsEnabledChanged) + // logout dialog Q_PROPERTY(bool allowLogout READ allowLogout READ allowLogout NOTIFY allowLogoutChanged) @@ -286,6 +289,14 @@ public: bool dynamicTilingEnabled() const; void setDynamicTilingEnabled(bool enabled); + /** + * Whether the SHIFT snap layout picker is enabled. + * Defaults to true; only takes effect in convergence mode when gaming mode + * and dynamic tiling are off. + */ + bool snapLayoutsEnabled() const; + void setSnapLayoutsEnabled(bool enabled); + /** * Whether logout button is shown in the logout/shutdown dialog. */ @@ -335,6 +346,7 @@ Q_SIGNALS: void gamingModeEnabledChanged(); void gamingDismissHintEnabledChanged(); void dynamicTilingEnabledChanged(); + void snapLayoutsEnabledChanged(); void allowLogoutChanged(); void lockscreenLeftButtonActionChanged(); void lockscreenRightButtonActionChanged(); diff --git a/kcms/mobileshell/ui/main.qml b/kcms/mobileshell/ui/main.qml index 431caf09..9b7f3ae5 100644 --- a/kcms/mobileshell/ui/main.qml +++ b/kcms/mobileshell/ui/main.qml @@ -95,7 +95,24 @@ KCM.SimpleKCM { } } - FormCard.FormDelegateSeparator { above: dynamicTilingSwitch; below: autoHidePanels } + FormCard.FormDelegateSeparator { above: dynamicTilingSwitch; below: snapLayoutsSwitch } + + FormCard.FormSwitchDelegate { + id: snapLayoutsSwitch + text: i18n("Snap Layouts") + description: i18n("Show the snap layout picker from the maximize button. Disabled while convergence mode is off, gaming mode is active, or dynamic tiling is enabled.") + enabled: ShellSettings.Settings.convergenceModeEnabled + && !ShellSettings.Settings.gamingModeEnabled + && !ShellSettings.Settings.dynamicTilingEnabled + checked: ShellSettings.Settings.snapLayoutsEnabled + onCheckedChanged: { + if (checked != ShellSettings.Settings.snapLayoutsEnabled) { + ShellSettings.Settings.snapLayoutsEnabled = checked; + } + } + } + + FormCard.FormDelegateSeparator { above: snapLayoutsSwitch; below: autoHidePanels } FormCard.FormSwitchDelegate { id: autoHidePanels diff --git a/kwin/effects/shift-snap-assist/contents/ui/main.qml b/kwin/effects/shift-snap-assist/contents/ui/main.qml index db880b95..7b5cf680 100644 --- a/kwin/effects/shift-snap-assist/contents/ui/main.qml +++ b/kwin/effects/shift-snap-assist/contents/ui/main.qml @@ -24,6 +24,7 @@ KWinComponents.SceneEffect { readonly property bool snapLayoutsEligible: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled && !ShellSettings.Settings.dynamicTilingEnabled + && ShellSettings.Settings.snapLayoutsEnabled readonly property int hoverBarHeight: 30 readonly property int decorationButtonSize: 16 readonly property int decorationButtonSpacing: 8 @@ -372,6 +373,12 @@ KWinComponents.SceneEffect { effect.hideSnapLayouts(); } } + + function onSnapLayoutsEnabledChanged() { + if (!effect.snapLayoutsEligible) { + effect.hideSnapLayouts(); + } + } } // ── Gap constant (must match shift-tiling) ────────────────────────────