From 55f778ebe86f53f81098a6c0ec1c4c6e5b6a9941 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Tue, 21 Apr 2026 09:08:49 +0200 Subject: [PATCH] Refine gaming quick settings visuals and toggles Expose missing gaming controls directly in the panel so they remain reachable in convergence mode. Add toggles for DND, Launch Hint, Night Color, and Perf Overlay with gamepad focus order updates. Restyle brightness and volume sliders with rounded Plasma-themed tracks and handles to remove harsh bar visuals while keeping existing behavior unchanged. --- .../folio/qml/gaming/GamingQuickSettings.qml | 269 ++++++++++++++++-- 1 file changed, 248 insertions(+), 21 deletions(-) diff --git a/containments/homescreens/folio/qml/gaming/GamingQuickSettings.qml b/containments/homescreens/folio/qml/gaming/GamingQuickSettings.qml index 937a694a..5c7cebae 100644 --- a/containments/homescreens/folio/qml/gaming/GamingQuickSettings.qml +++ b/containments/homescreens/folio/qml/gaming/GamingQuickSettings.qml @@ -9,10 +9,13 @@ import org.kde.kirigami as Kirigami import org.kde.plasma.components 3.0 as PC3 import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell.gamingshellplugin as GamingShell +import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.screenbrightnessplugin as ScreenBrightness import org.kde.plasma.private.volume import org.kde.plasma.networkmanagement as PlasmaNM import org.kde.bluezqt 1.0 as BluezQt +import org.kde.plasma.private.mobileshell.state as MobileShellState +import org.kde.plasma.quicksetting.nightcolor as NightColor Item { id: root @@ -26,8 +29,13 @@ Item { function _buildControlsList() { var list = [] + if (GamingShell.PowerProfileControl.available && performanceSection._availableProfiles.length > 0) list.push(profileRow) if (screenBrightness.brightnessAvailable) list.push(brightnessSlider) if (PreferredDevice.sink) list.push(volumeSlider) + list.push(dndSwitch) + list.push(launchHintSwitch) + list.push(nightColorSwitch) + list.push(overlaySwitch) list.push(wifiSwitch) list.push(btSwitch) list.push(airplaneSwitch) @@ -37,7 +45,7 @@ Item { function open() { opened = true _buildControlsList() - _focusIndex = 0 + _focusIndex = Math.max(0, Math.min(_focusIndex, _controls.length - 1)) _highlightCurrent() } function close() { @@ -68,26 +76,38 @@ Item { } function gamepadLeft() { var ctrl = _controls[_focusIndex] - if (ctrl instanceof PC3.Slider) { + if (typeof ctrl.decrease === "function") { ctrl.decrease() - ctrl.moved() + if (typeof ctrl.moved === "function") ctrl.moved() } } function gamepadRight() { var ctrl = _controls[_focusIndex] - if (ctrl instanceof PC3.Slider) { + if (typeof ctrl.increase === "function") { ctrl.increase() - ctrl.moved() + if (typeof ctrl.moved === "function") ctrl.moved() } } function gamepadAccept() { var ctrl = _controls[_focusIndex] + if (ctrl === profileRow) { + ctrl.increase() + return + } if (ctrl instanceof QQC2.Switch) { ctrl.toggle() ctrl.toggled() } } + onOpenedChanged: { + if (opened) { + _buildControlsList() + _focusIndex = Math.max(0, Math.min(_focusIndex, _controls.length - 1)) + _highlightCurrent() + } + } + // Eat clicks on the dimmed backdrop MouseArea { anchors.fill: parent @@ -176,6 +196,96 @@ Item { Kirigami.Separator { Layout.fillWidth: true } + // ---- Performance Profile ---- + ColumnLayout { + id: performanceSection + Layout.fillWidth: true + spacing: Kirigami.Units.smallSpacing + visible: GamingShell.PowerProfileControl.available + && _availableProfiles.length > 0 + + PC3.Label { + text: i18n("Performance") + font.bold: true + } + + // Ordered low-to-high so gamepad left=slower, right=faster + readonly property var _profileOrder: ["power-saver", "balanced", "performance"] + readonly property var _availableProfiles: { + var ordered = [] + for (var i = 0; i < _profileOrder.length; i++) { + if (GamingShell.PowerProfileControl.profiles.indexOf(_profileOrder[i]) >= 0) { + ordered.push(_profileOrder[i]) + } + } + return ordered + } + + Item { + id: profileRow + focus: true + Layout.fillWidth: true + Layout.preferredHeight: profileButtons.implicitHeight + + function decrease() { + var profiles = parent._availableProfiles + var idx = profiles.indexOf(GamingShell.PowerProfileControl.activeProfile) + if (idx > 0) { + GamingShell.PowerProfileControl.activeProfile = profiles[idx - 1] + } + } + function increase() { + var profiles = parent._availableProfiles + var idx = profiles.indexOf(GamingShell.PowerProfileControl.activeProfile) + if (idx >= 0 && idx < profiles.length - 1) { + GamingShell.PowerProfileControl.activeProfile = profiles[idx + 1] + } + } + + Rectangle { + anchors.fill: parent + anchors.margins: -Kirigami.Units.smallSpacing + radius: Kirigami.Units.smallSpacing + color: "transparent" + border.color: Kirigami.Theme.highlightColor + border.width: parent.activeFocus ? 2 : 0 + } + + RowLayout { + id: profileButtons + anchors.left: parent.left + anchors.right: parent.right + spacing: Kirigami.Units.smallSpacing + + Repeater { + model: performanceSection._availableProfiles + + QQC2.Button { + Layout.fillWidth: true + text: { + switch (modelData) { + case "performance": return i18n("Performance") + case "balanced": return i18n("Balanced") + case "power-saver": return i18n("Power Saver") + default: return modelData + } + } + icon.name: { + switch (modelData) { + case "performance": return "speedometer" + case "balanced": return "system-suspend-hibernate" + case "power-saver": return "battery-profile-powersave" + default: return "" + } + } + highlighted: GamingShell.PowerProfileControl.activeProfile === modelData + onClicked: GamingShell.PowerProfileControl.activeProfile = modelData + } + } + } + } + } + // ---- Brightness ---- ColumnLayout { Layout.fillWidth: true @@ -213,14 +323,42 @@ Item { onTriggered: brightnessSlider.value = Qt.binding(() => screenBrightness.brightness) } - // Focus highlight - Rectangle { - anchors.fill: parent - anchors.margins: -Kirigami.Units.smallSpacing - radius: Kirigami.Units.smallSpacing - color: "transparent" - border.color: Kirigami.Theme.highlightColor - border.width: parent.activeFocus ? 2 : 0 + // Keep Plasma/Kirigami colors while using a cleaner rounded style. + background: Rectangle { + x: brightnessSlider.leftPadding + y: brightnessSlider.topPadding + brightnessSlider.availableHeight / 2 - height / 2 + width: brightnessSlider.availableWidth + height: Kirigami.Units.smallSpacing + 2 + radius: height / 2 + color: Kirigami.Theme.alternateBackgroundColor + + Rectangle { + width: parent.width * brightnessSlider.visualPosition + height: parent.height + radius: parent.radius + color: Kirigami.Theme.highlightColor + } + + Rectangle { + anchors.fill: parent + radius: parent.radius + color: "transparent" + border.color: Kirigami.Theme.highlightColor + border.width: brightnessSlider.activeFocus ? 1 : 0 + } + } + + handle: Rectangle { + x: brightnessSlider.leftPadding + brightnessSlider.visualPosition * (brightnessSlider.availableWidth - width) + y: brightnessSlider.topPadding + brightnessSlider.availableHeight / 2 - height / 2 + implicitWidth: Kirigami.Units.iconSizes.small + implicitHeight: Kirigami.Units.iconSizes.small + radius: width / 2 + color: Kirigami.Theme.backgroundColor + border.color: brightnessSlider.pressed + ? Kirigami.Theme.highlightColor + : Kirigami.Theme.disabledTextColor + border.width: brightnessSlider.activeFocus || brightnessSlider.pressed ? 2 : 1 } } @@ -267,14 +405,42 @@ Item { } } - // Focus highlight - Rectangle { - anchors.fill: parent - anchors.margins: -Kirigami.Units.smallSpacing - radius: Kirigami.Units.smallSpacing - color: "transparent" - border.color: Kirigami.Theme.highlightColor - border.width: parent.activeFocus ? 2 : 0 + // Keep Plasma/Kirigami colors while using a cleaner rounded style. + background: Rectangle { + x: volumeSlider.leftPadding + y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2 + width: volumeSlider.availableWidth + height: Kirigami.Units.smallSpacing + 2 + radius: height / 2 + color: Kirigami.Theme.alternateBackgroundColor + + Rectangle { + width: parent.width * volumeSlider.visualPosition + height: parent.height + radius: parent.radius + color: Kirigami.Theme.highlightColor + } + + Rectangle { + anchors.fill: parent + radius: parent.radius + color: "transparent" + border.color: Kirigami.Theme.highlightColor + border.width: volumeSlider.activeFocus ? 1 : 0 + } + } + + handle: Rectangle { + x: volumeSlider.leftPadding + volumeSlider.visualPosition * (volumeSlider.availableWidth - width) + y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2 + implicitWidth: Kirigami.Units.iconSizes.small + implicitHeight: Kirigami.Units.iconSizes.small + radius: width / 2 + color: Kirigami.Theme.backgroundColor + border.color: volumeSlider.pressed + ? Kirigami.Theme.highlightColor + : Kirigami.Theme.disabledTextColor + border.width: volumeSlider.activeFocus || volumeSlider.pressed ? 2 : 1 } } @@ -288,6 +454,67 @@ Item { Kirigami.Separator { Layout.fillWidth: true } + // ---- Gaming Tweaks ---- + PC3.Label { + text: i18n("Gaming") + font.bold: true + } + + GridLayout { + Layout.fillWidth: true + columns: 2 + rowSpacing: Kirigami.Units.smallSpacing + columnSpacing: Kirigami.Units.largeSpacing + + QQC2.Switch { + id: dndSwitch + text: i18n("Do Not Disturb") + checked: MobileShellState.ShellDBusClient.doNotDisturb + onToggled: MobileShellState.ShellDBusClient.doNotDisturb = checked + } + + QQC2.Switch { + id: launchHintSwitch + text: i18n("Launch Hint") + checked: ShellSettings.Settings.gamingDismissHintEnabled + onToggled: ShellSettings.Settings.gamingDismissHintEnabled = checked + } + + QQC2.Switch { + id: nightColorSwitch + text: i18n("Night Color") + checked: NightColor.NightColorUtil.enabled + onToggled: NightColor.NightColorUtil.enabled = checked + } + + QQC2.Switch { + id: overlaySwitch + text: i18n("Perf Overlay") + checked: GamingShell.GameLauncherProvider.overlayEnabled + onToggled: GamingShell.GameLauncherProvider.overlayEnabled = checked + } + + // GameMode status (auto-managed, read-only indicator) + RowLayout { + spacing: Kirigami.Units.smallSpacing + visible: GamingShell.GameModeControl.available + + Kirigami.Icon { + implicitWidth: Kirigami.Units.iconSizes.small + implicitHeight: Kirigami.Units.iconSizes.small + source: "games-achievements" + } + PC3.Label { + text: GamingShell.GameModeControl.active + ? i18n("GameMode active") + : i18n("GameMode standby") + opacity: 0.7 + } + } + } + + Kirigami.Separator { Layout.fillWidth: true } + // ---- Connectivity toggles ---- PC3.Label { text: i18n("Connectivity")