Merge branch 'main' into feat/dashboard-media-widget
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 18s
JS/TS / lint (pull_request) Successful in 17s
Python / static (pull_request) Successful in 58s
Rust / fmt (pull_request) Successful in 1m4s
C++ / build (pull_request) Successful in 2m39s
Rust / build (pull_request) Successful in 2m22s
Rust / clippy (pull_request) Successful in 1m48s
Python / verify (pull_request) Successful in 3m29s
C++ / clang-tidy (pull_request) Successful in 7m7s

This commit is contained in:
2026-08-19 00:36:42 +02:00
5 changed files with 237 additions and 74 deletions
@@ -1,52 +1,118 @@
import Quickshell.Bluetooth pragma ComponentBehavior: Bound
import Quickshell.Networking as QSNetwork
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import Quickshell.Networking as QSNetwork
import ZShell.Components import ZShell.Components
import qs.Components
import ZShell.Config import ZShell.Config
import qs.Components
import qs.Services
import qs.Helpers import qs.Helpers
import qs.Daemons import qs.Daemons
import qs.Services import qs.Modules.Settings
import qs.Modules.Bar.Popouts as BarPopouts
CustomRect { CustomRect {
id: root id: root
required property Item popouts readonly property bool needExtraRow: quickToggles.length > 6
required property var visibilities required property BarPopouts.Wrapper popouts
readonly property var quickToggles: {
const seenIds = new Set();
return Config.utilities.quickToggles.values.filter(item => {
if (!item.enabled)
return false;
if (seenIds.has(item.id)) {
return false;
}
if (item.id === "bluetooth" && !Bluetooth.defaultAdapter)
return false;
if (item.id === "wifi" && !QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi))
return false;
seenIds.add(item.id);
return true;
});
}
readonly property int splitIndex: Math.ceil(quickToggles.length / 2)
required property PersistentProperties visibilities
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + 18 * 2 implicitHeight: layout.implicitHeight + Tokens.padding.extraLargeIncreased
radius: Tokens.rounding.small radius: Tokens.rounding.smallest
ButtonRow { ColumnLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
anchors.margins: 18 anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.normal
Toggle { CustomText {
font.pointSize: Tokens.font.size.normal
text: qsTr("Quick Toggles")
}
QuickToggleRow {
model: root.needExtraRow ? root.quickToggles.slice(0, root.splitIndex) : root.quickToggles
}
QuickToggleRow {
model: root.needExtraRow ? root.quickToggles.slice(root.splitIndex) : []
visible: root.needExtraRow
}
}
component QuickToggleRow: ButtonRow {
property alias model: repeater.model
Layout.fillWidth: true
spacing: Tokens.spacing.small
Repeater {
id: repeater
delegate: DelegateChooser {
role: "id"
DelegateChoice {
roleValue: "wifi"
delegate: Toggle {
checked: Network.wifiEnabled checked: Network.wifiEnabled
icon: Network.wifiEnabled ? "wifi" : "wifi_off" icon: checked ? "wifi" : "wifi_off"
visible: QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi)
onClicked: Network.toggleWifi() onClicked: Network.toggleWifi()
} }
Toggle {
id: toggle
checked: !NotifServer.dnd
icon: NotifServer.dnd ? "notifications_off" : "notifications"
onClicked: NotifServer.dnd = !NotifServer.dnd
} }
Toggle { DelegateChoice {
roleValue: "bluetooth"
delegate: Toggle {
checked: Bluetooth.defaultAdapter?.enabled ?? false // qmllint disable unresolved-type
icon: "bluetooth"
onClicked: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
if (adapter)
adapter.enabled = !adapter.enabled;
}
}
}
DelegateChoice {
roleValue: "mic"
delegate: Toggle {
checked: !Audio.sourceMuted checked: !Audio.sourceMuted
icon: Audio.sourceMuted ? "mic_off" : "mic" icon: checked ? "mic" : "mic_off"
onClicked: { onClicked: {
const audio = Audio.source?.audio; const audio = Audio.source?.audio;
@@ -54,10 +120,14 @@ CustomRect {
audio.muted = !audio.muted; audio.muted = !audio.muted;
} }
} }
}
Toggle { DelegateChoice {
roleValue: "audio"
delegate: Toggle {
checked: !Audio.muted checked: !Audio.muted
icon: Audio.muted ? "volume_off" : "volume_up" icon: checked ? "volume_up" : "volume_off"
onClicked: { onClicked: {
const audio = Audio.sink?.audio; const audio = Audio.sink?.audio;
@@ -65,37 +135,50 @@ CustomRect {
audio.muted = !audio.muted; audio.muted = !audio.muted;
} }
} }
Toggle {
checked: Bluetooth.defaultAdapter?.enabled ?? false
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
visible: Bluetooth.defaultAdapter ?? false
onClicked: {
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.enabled = !adapter.enabled;
}
} }
Toggle { DelegateChoice {
roleValue: "gameMode"
delegate: Toggle {
checked: GameMode.enabled checked: GameMode.enabled
icon: GameMode.enabled ? "videogame_asset" : "videogame_asset_off" icon: "gamepad"
onClicked: GameMode.enabled = !GameMode.enabled onClicked: GameMode.enabled = !GameMode.enabled
} }
} }
CustomShortcut { DelegateChoice {
name: "toggle-dnd" roleValue: "dnd"
onPressed: { delegate: Toggle {
toggle.clicked(); checked: NotifServer.dnd
icon: "notifications_off"
onClicked: NotifServer.dnd = !NotifServer.dnd
} }
} }
DelegateChoice {
roleValue: "settings"
delegate: Toggle {
icon: "settings"
inactiveOnColor: Colors.palette.m3onSurfaceVariant
isToggle: false
onClicked: {
root.visibilities.sidebar = false;
root.visibilities.settings = true;
}
}
}
}
}
}
component Toggle: IconButton { component Toggle: IconButton {
fillWidth: true fillWidth: true
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
isRound: true isRound: true
isToggle: true isToggle: true
shapeMorph: true shapeMorph: true
@@ -1,13 +1,14 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Modules.Bar.Popouts as BarPopouts
import qs.Modules.Notifications.Sidebar.Utils.Cards import qs.Modules.Notifications.Sidebar.Utils.Cards
import ZShell.Config import ZShell.Config
Item { Item {
id: root id: root
required property Item popouts required property BarPopouts.Wrapper popouts
required property PersistentProperties props required property PersistentProperties props
required property var visibilities required property var visibilities
@@ -30,6 +31,7 @@ Item {
} }
Toggles { Toggles {
Layout.fillWidth: true
popouts: root.popouts popouts: root.popouts
visibilities: root.visibilities visibilities: root.visibilities
} }
@@ -1,15 +1,16 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.Components
import ZShell.Config
import Quickshell
import QtQuick import QtQuick
import Quickshell
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Popouts as BarPopouts
Item { Item {
id: root id: root
property real offsetScale: shouldBeActive ? 0 : 1 property real offsetScale: shouldBeActive ? 0 : 1
required property Item popouts required property BarPopouts.Wrapper popouts
readonly property PersistentProperties props: PersistentProperties { readonly property PersistentProperties props: PersistentProperties {
property string recordingConfirmDelete property string recordingConfirmDelete
property bool recordingListExpanded: false property bool recordingListExpanded: false
@@ -9,6 +9,16 @@ import qs.Modules.Settings.Common
PageBase { PageBase {
id: root id: root
readonly property var builtinIcons: ({
audio: qsTr("Toggle audio"),
mic: qsTr("Toggle microphone"),
settings: qsTr("Open settings"),
dnd: qsTr("Toggle Do-Not-Disturb"),
gameMode: qsTr("Toggle game mode"),
bluetooth: qsTr("Toggle bluetooth"),
wifi: qsTr("Toggle Wi-Fi")
})
isSubPage: true isSubPage: true
title: qsTr("Sidebar") title: qsTr("Sidebar")
@@ -44,5 +54,59 @@ PageBase {
onMoved: v => Config.sidebar.dragThreshold = v onMoved: v => Config.sidebar.dragThreshold = v
} }
SectionHeader {
text: qsTr("Quick toggles")
}
ListEditor {
function labelFor(item: var): string {
const prettyName = root.builtinIcons[item.id];
if (prettyName)
return prettyName;
const label = item.id.replace(/([A-Z])/g, " $1");
return label.charAt(0).toUpperCase() + label.slice(1).toLowerCase();
}
function toggledFor(item: var): bool {
return item.enabled;
}
first: true
values: Config.utilities.quickToggles.values
z: 1
onItemMoved: (from, to) => Config.utilities.quickToggles.move(from, to)
onItemRemoved: index => Config.utilities.quickToggles.remove(index)
onItemToggled: (index, checked) => Config.utilities.quickToggles.at(index).enabled = checked
}
DialogSelectButton {
id: addItemContainer
acceptLabel: qsTr("Add")
enabled: Object.keys(model).length > 0
header: qsTr("Add new entry")
icon: "add"
label: qsTr("Add entry")
model: {
const present = new Set(Config.utilities.quickToggles.values.map(item => item.id));
return Object.keys(root.builtinIcons).filter(id => !present.has(id)).map(id => ({
id: id,
label: root.builtinIcons[id]
}));
}
rootParent: root.flickable
onAccepted: {
if (!selectedItem)
return;
Config.utilities.quickToggles.insert({
id: selectedItem,
enabled: true
});
}
}
} }
} }
+13
View File
@@ -1,5 +1,8 @@
#pragma once #pragma once
#include "configobject.hpp" #include "configobject.hpp"
#include "configlist.hpp"
#include <QVariantList> #include <QVariantList>
#include <QStringList> #include <QStringList>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@@ -58,6 +61,16 @@ class Utilities : public ConfigObject {
CONFIG_SUBOBJECT(UtilitiesSizes, sizes) CONFIG_SUBOBJECT(UtilitiesSizes, sizes)
CONFIG_SUBOBJECT(Toasts, toasts) CONFIG_SUBOBJECT(Toasts, toasts)
CONFIG_SUBOBJECT(Vpn, vpn) CONFIG_SUBOBJECT(Vpn, vpn)
CONFIG_LIST(
EntryList,
quickToggles,
{LIST_ENTRY(dnd, true),
LIST_ENTRY(wifi, true),
LIST_ENTRY(settings, true),
LIST_ENTRY(bluetooth, true),
LIST_ENTRY(mic, true),
LIST_ENTRY(audio, true),
LIST_ENTRY(gameMode, true)})
public: public:
explicit Utilities(QObject* parent = nullptr) explicit Utilities(QObject* parent = nullptr)