nosignal-shell/modules/utilities/cards/Toggles.qml
2026-05-02 18:56:56 +10:00

170 lines
5.7 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell.Bluetooth
import Caelestia.Config
import qs.components
import qs.components.controls
import qs.services
import qs.modules.bar.popouts as BarPopouts
StyledRect {
id: root
required property DrawerVisibilities visibilities
required property BarPopouts.Wrapper popouts
readonly property var quickToggles: {
const seenIds = new Set();
return Config.utilities.quickToggles.filter(item => {
if (!(item.enabled ?? true))
return false;
if (seenIds.has(item.id)) {
return false;
}
if (item.id === "vpn") {
return GlobalConfig.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 + Tokens.padding.extraLargeIncreased
radius: Tokens.rounding.large
color: Colours.tPalette.m3surfaceContainer
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.medium
StyledText {
text: qsTr("Quick Toggles")
font: Tokens.font.body.medium
}
QuickToggleRow {
rowModel: root.needExtraRow ? root.quickToggles.slice(0, root.splitIndex) : root.quickToggles
}
QuickToggleRow {
visible: root.needExtraRow
rowModel: root.needExtraRow ? root.quickToggles.slice(root.splitIndex) : []
}
}
component QuickToggleRow: RowLayout {
property var rowModel: []
Layout.fillWidth: true
spacing: Tokens.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 // qmllint disable unresolved-type
onClicked: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
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
isToggle: 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 && VPN.status.state !== "needs-auth" && VPN.status.state !== "error"
enabled: !VPN.connecting
isToggle: VPN.status.state !== "needs-auth" && VPN.status.state !== "error"
inactiveOnColour: Colours.palette.m3onSurfaceVariant
onClicked: VPN.toggle()
}
}
}
}
}
component Toggle: IconButton {
Layout.fillWidth: true
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? Tokens.padding.large : internalChecked ? Tokens.padding.small : 0)
radius: stateLayer.pressed ? Tokens.rounding.medium / 2 : internalChecked ? Tokens.rounding.medium : Tokens.rounding.large
inactiveColour: Colours.layer(Colours.palette.m3surfaceContainerHighest, 2)
isToggle: true
radiusAnim.duration: Tokens.anim.durations.expressiveFastSpatial
radiusAnim.easing: Tokens.anim.expressiveFastSpatial
Behavior on Layout.preferredWidth {
Anim {
type: Anim.FastSpatial
}
}
}
}