import qs.components import qs.components.controls import qs.services import qs.config import qs.modules.controlcenter import Quickshell import Quickshell.Bluetooth import QtQuick import QtQuick.Layouts StyledRect { id: root required property var visibilities required property Item popouts readonly property var quickToggles: { const seenIds = new Set(); return Config.utilities.quickToggles.filter(item => { if (!item.enabled) return false; if (seenIds.has(item.id)) { return false; } if (item.id === "vpn") { return Config.utilities.vpn.provider.some(p => typeof p === "object" ? (p.enabled === true) : false ); } seenIds.add(item.id); return true; }); } readonly property int splitIndex: Math.ceil(quickToggles.length / 2) readonly property bool needExtraRow: quickToggles.length > 6 Layout.fillWidth: true implicitHeight: layout.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal color: Colours.tPalette.m3surfaceContainer ColumnLayout { id: layout anchors.fill: parent anchors.margins: Appearance.padding.large spacing: Appearance.spacing.normal StyledText { text: qsTr("Quick Toggles") font.pointSize: Appearance.font.size.normal } ToggleRow { rowModel: root.needExtraRow ? root.quickToggles.slice(0, root.splitIndex) : root.quickToggles } ToggleRow { visible: root.needExtraRow rowModel: root.needExtraRow ? root.quickToggles.slice(root.splitIndex) : [] } } component ToggleRow: RowLayout { property var rowModel: [] Layout.fillWidth: true spacing: Appearance.spacing.small Repeater { model: parent.rowModel delegate: DelegateChooser { role: "id" DelegateChoice { roleValue: "wifi" delegate: Toggle { icon: "wifi" checked: Nmcli.wifiEnabled onClicked: Nmcli.toggleWifi() } } DelegateChoice { roleValue: "bluetooth" delegate: Toggle { icon: "bluetooth" checked: Bluetooth.defaultAdapter?.enabled ?? false onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.enabled = !adapter.enabled; } } } DelegateChoice { roleValue: "mic" delegate: Toggle { icon: "mic" checked: !Audio.sourceMuted onClicked: { const audio = Audio.source?.audio; if (audio) audio.muted = !audio.muted; } } } DelegateChoice { roleValue: "settings" delegate: Toggle { icon: "settings" inactiveOnColour: Colours.palette.m3onSurfaceVariant toggle: false onClicked: { root.visibilities.utilities = false; root.popouts.detach("network"); } } } DelegateChoice { roleValue: "gameMode" delegate: Toggle { icon: "gamepad" checked: GameMode.enabled onClicked: GameMode.enabled = !GameMode.enabled } } DelegateChoice { roleValue: "dnd" delegate: Toggle { icon: "notifications_off" checked: Notifs.dnd onClicked: Notifs.dnd = !Notifs.dnd } } DelegateChoice { roleValue: "vpn" delegate: Toggle { icon: "vpn_key" checked: VPN.connected enabled: !VPN.connecting onClicked: VPN.toggle() } } } } } component Toggle: IconButton { Layout.fillWidth: true Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? Appearance.padding.large : internalChecked ? Appearance.padding.smaller : 0) radius: stateLayer.pressed ? Appearance.rounding.small / 2 : internalChecked ? Appearance.rounding.small : Appearance.rounding.normal inactiveColour: Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) toggle: true radiusAnim.duration: Appearance.anim.durations.expressiveFastSpatial radiusAnim.easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial Behavior on Layout.preferredWidth { Anim { duration: Appearance.anim.durations.expressiveFastSpatial easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial } } } }