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.
This commit is contained in:
Marco Allegretti 2026-04-21 09:08:49 +02:00
parent 62243b7f64
commit 55f778ebe8

View file

@ -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")