refractor quick toggles data, add volume slider for waffles action center

This commit is contained in:
end-4
2025-11-17 23:43:25 +01:00
parent 9228165428
commit fcee7ce6f9
55 changed files with 939 additions and 470 deletions
@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.47 4.22a.75.75 0 0 0 0 1.06L15.19 12l-6.72 6.72a.75.75 0 1 0 1.06 1.06l7.25-7.25a.75.75 0 0 0 0-1.06L9.53 4.22a.75.75 0 0 0-1.06 0Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 262 B

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M14.704 3.442c.191.226.296.512.296.808v7.25a6.495 6.495 0 0 0-1.5.878V4.789L8.525 9H4.25a.75.75 0 0 0-.75.75v4.5c0 .415.336.75.75.75h4.275l2.487 2.107a6.576 6.576 0 0 0 .23 2.16L7.976 16.5H4.25A2.25 2.25 0 0 1 2 14.25v-4.5A2.25 2.25 0 0 1 4.25 7.5h3.725l4.968-4.204a1.25 1.25 0 0 1 1.761.147Zm-.426 10.535a2 2 0 0 1-1.441 2.496l-.584.145a5.729 5.729 0 0 0 .006 1.807l.54.13a2 2 0 0 1 1.45 2.51l-.187.631c.44.386.94.7 1.484.922l.494-.519a2 2 0 0 1 2.899 0l.498.526a5.28 5.28 0 0 0 1.483-.913l-.198-.686a2 2 0 0 1 1.441-2.497l.584-.144a5.716 5.716 0 0 0-.006-1.807l-.54-.13a2 2 0 0 1-1.45-2.51l.187-.632a5.282 5.282 0 0 0-1.484-.921l-.493.518a2 2 0 0 1-2.9 0l-.498-.525c-.544.22-1.044.53-1.483.913l.198.686ZM17.5 19c-.8 0-1.45-.671-1.45-1.5 0-.828.65-1.5 1.45-1.5.8 0 1.45.672 1.45 1.5 0 .829-.65 1.5-1.45 1.5Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 936 B

@@ -572,7 +572,7 @@ Singleton {
}
property JsonObject waffles: JsonObject {
// Animations on Windoes are kinda janky. Set the following to
// Animations on Windoes are kinda janky. Setting the following to
// false will make (some) stuff also be like that for accuracy.
// Example: the right-click menu of the Start button
property bool smootherAnimations: true
@@ -580,6 +580,9 @@ Singleton {
property bool bottom: true
property bool leftAlignApps: false
}
property JsonObject actionCenter: JsonObject {
property list<string> toggles: []
}
}
}
}
@@ -0,0 +1,17 @@
import QtQuick
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Anti-flashbang")
tooltipText: Translation.tr("Anti-flashbang")
icon: "flash_off"
toggled: Config.options.light.antiFlashbang.enable
mainAction: () => {
Config.options.light.antiFlashbang.enable = !Config.options.light.antiFlashbang.enable;
}
hasMenu: true
}
@@ -0,0 +1,17 @@
import QtQuick
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Audio output")
statusText: toggled ? Translation.tr("Unmuted") : Translation.tr("Muted")
tooltipText: Translation.tr("Audio output | Right-click for volume mixer & device selector")
toggled: !Audio.sink?.audio?.muted
icon: Audio.sink?.audio?.muted ? "volume_off" : "volume_up"
mainAction: () => {
Audio.toggleMute()
}
hasMenu: true
}
@@ -0,0 +1,23 @@
import QtQuick
import Quickshell.Bluetooth
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Bluetooth")
statusText: BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("No device")
tooltipText: Translation.tr("%1 | Right-click to configure").arg(
(BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("Bluetooth"))
+ (BluetoothStatus.activeDeviceCount > 1 ? ` +${BluetoothStatus.activeDeviceCount - 1}` : "")
)
icon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
available: BluetoothStatus.available
toggled: BluetoothStatus.enabled
mainAction: () => {
Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter?.enabled
}
hasMenu: true
}
@@ -0,0 +1,78 @@
import QtQuick
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import Quickshell
import Quickshell.Io
QuickToggleModel {
id: root
name: Translation.tr("Cloudflare WARP")
toggled: false
icon: "cloud_lock"
mainAction: () => {
if (toggled) {
root.toggled = false
Quickshell.execDetached(["warp-cli", "disconnect"])
} else {
root.toggled = true
Quickshell.execDetached(["warp-cli", "connect"])
}
}
Process {
id: connectProc
command: ["warp-cli", "connect"]
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
Quickshell.execDetached(["notify-send",
Translation.tr("Cloudflare WARP"),
Translation.tr("Connection failed. Please inspect manually with the <tt>warp-cli</tt> command")
, "-a", "Shell"
])
}
}
}
Process {
id: registrationProc
command: ["warp-cli", "registration", "new"]
onExited: (exitCode, exitStatus) => {
console.log("Warp registration exited with code and status:", exitCode, exitStatus)
if (exitCode === 0) {
connectProc.running = true
} else {
Quickshell.execDetached(["notify-send",
Translation.tr("Cloudflare WARP"),
Translation.tr("Registration failed. Please inspect manually with the <tt>warp-cli</tt> command"),
"-a", "Shell"
])
}
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", "warp-cli status"]
stdout: StdioCollector {
id: warpStatusCollector
onStreamFinished: {
if (warpStatusCollector.text.length > 0) {
root.available = true
}
if (warpStatusCollector.text.includes("Unable")) {
registrationProc.running = true
} else if (warpStatusCollector.text.includes("Connected")) {
root.toggled = true
} else if (warpStatusCollector.text.includes("Disconnected")) {
root.toggled = false
}
}
}
}
tooltipText: Translation.tr("Cloudflare WARP (1.1.1.1)")
}
@@ -0,0 +1,29 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Color picker")
hasStatusText: false
toggled: false
icon: "colorize"
mainAction: () => {
GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start();
}
Timer {
id: delayedActionTimer
interval: 300
repeat: false
onTriggered: {
Quickshell.execDetached(["hyprpicker", "-a"]);
}
}
tooltipText: Translation.tr("Color picker")
}
@@ -0,0 +1,25 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Dark Mode")
statusText: Appearance.m3colors.darkmode ? Translation.tr("Dark") : Translation.tr("Light")
toggled: Appearance.m3colors.darkmode
icon: "contrast"
mainAction: () => {
if (Appearance.m3colors.darkmode) {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
} else {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
}
}
tooltipText: Translation.tr("Dark Mode")
}
@@ -0,0 +1,30 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("EasyEffects")
available: EasyEffects.available
toggled: EasyEffects.active
icon: "graphic_eq"
Component.onCompleted: {
EasyEffects.fetchActiveState()
}
mainAction: () => {
EasyEffects.toggle()
}
altAction: () => {
Quickshell.execDetached(["bash", "-c", "flatpak run com.github.wwmm.easyeffects || easyeffects"])
GlobalStates.sidebarRightOpen = false
}
tooltipText: Translation.tr("EasyEffects | Right-click to configure")
}
@@ -0,0 +1,33 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
id: root
name: Translation.tr("Game mode")
toggled: toggled
icon: "gamepad"
mainAction: () => {
root.toggled = !root.toggled
if (root.toggled) {
Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`])
} else {
Quickshell.execDetached(["hyprctl", "reload"])
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`]
onExited: (exitCode, exitStatus) => {
root.toggled = exitCode !== 0 // Inverted because enabled = nonzero exit
}
}
tooltipText: Translation.tr("Game mode")
}
@@ -0,0 +1,18 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Keep awake")
toggled: Idle.inhibit
icon: "coffee"
mainAction: () => {
Idle.toggleInhibit()
}
tooltipText: Translation.tr("Keep system awake")
}
@@ -0,0 +1,20 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Audio input")
statusText: toggled ? Translation.tr("Enabled") : Translation.tr("Muted")
toggled: !Audio.source?.audio?.muted
icon: Audio.source?.audio?.muted ? "mic_off" : "mic"
mainAction: () => {
Audio.toggleMicMute()
}
hasMenu: true
tooltipText: Translation.tr("Audio input | Right-click for volume mixer & device selector")
}
@@ -0,0 +1,25 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
toggled: SongRec.running
property bool sourceIsMonitor: SongRec.monitorSource === SongRec.MonitorSource.Monitor
name: Translation.tr("Identify Music")
statusText: toggled ? Translation.tr("Listening...") : sourceIsMonitor ? Translation.tr("System sound") : Translation.tr("Microphone")
icon: toggled ? "music_cast" : (sourceIsMonitor ? "music_note" : "frame_person_mic")
tooltipText: Translation.tr("Recognize music | Right-click to toggle source")
mainAction: () => {
SongRec.toggleRunning()
}
altAction: () => {
SongRec.toggleMonitorSource()
}
}
@@ -0,0 +1,16 @@
import QtQuick
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Internet")
statusText: Network.networkName
tooltipText: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
icon: Network.materialSymbol
toggled: Network.wifiStatus !== "disabled"
mainAction: () => Network.toggleWifi()
hasMenu: true
}
@@ -0,0 +1,28 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
property bool auto: Config.options.light.night.automatic
name: Translation.tr("Night Light")
statusText: (auto ? Translation.tr("Auto, ") : "") + (toggled ? Translation.tr("Active") : Translation.tr("Inactive"))
toggled: Hyprsunset.active
icon: auto ? "night_sight_auto" : "bedtime"
mainAction: () => {
Hyprsunset.toggle()
}
hasMenu: true
Component.onCompleted: {
Hyprsunset.fetchState()
}
tooltipText: Translation.tr("Night Light | Right-click to configure")
}
@@ -0,0 +1,20 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Notifications")
statusText: toggled ? Translation.tr("Show") : Translation.tr("Silent")
toggled: !Notifications.silent
icon: toggled ? "notifications_active" : "notifications_paused"
mainAction: () => {
Notifications.silent = !Notifications.silent;
}
tooltipText: Translation.tr("Show notifications")
}
@@ -0,0 +1,19 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Virtual Keyboard")
toggled: GlobalStates.oskOpen
icon: toggled ? "keyboard_hide" : "keyboard"
mainAction: () => {
GlobalStates.oskOpen = !GlobalStates.oskOpen
}
tooltipText: Translation.tr("On-screen keyboard")
}
@@ -0,0 +1,39 @@
import QtQuick
import Quickshell
import Quickshell.Services.UPower
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Power Profile")
toggled: PowerProfiles.profile !== PowerProfile.Balanced
icon: switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "energy_savings_leaf"
case PowerProfile.Balanced: return "settings_slow_motion"
case PowerProfile.Performance: return "local_fire_department"
}
statusText: switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "Power Saver"
case PowerProfile.Balanced: return "Balanced"
case PowerProfile.Performance: return "Performance"
}
mainAction: () => {
if (PowerProfiles.hasPerformanceProfile) {
switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: PowerProfiles.profile = PowerProfile.Balanced
break;
case PowerProfile.Balanced: PowerProfiles.profile = PowerProfile.Performance
break;
case PowerProfile.Performance: PowerProfiles.profile = PowerProfile.PowerSaver
break;
}
} else {
PowerProfiles.profile = PowerProfiles.profile == PowerProfile.Balanced ? PowerProfile.PowerSaver : PowerProfile.Balanced
}
}
tooltipText: Translation.tr("Click to cycle through power profiles")
}
@@ -0,0 +1,22 @@
import QtQuick
QtObject {
// Textual info
required property string name
property string statusText
property string tooltipText: ""
property string icon: "close"
// State
property bool hasStatusText: true
property bool available: true
property bool toggled: false
// Interactions
required property var mainAction
property bool hasMenu: false
property var altAction: null
// Allow stuff like Processes to be declared freely
default property list<QtObject> data
}
@@ -0,0 +1,29 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
QuickToggleModel {
name: Translation.tr("Screen snip")
hasStatusText: false
toggled: false
icon: "screenshot_region"
mainAction: () => {
GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start();
}
Timer {
id: delayedActionTimer
interval: 300
repeat: false
onTriggered: {
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
}
}
tooltipText: Translation.tr("Screen snip")
}
@@ -0,0 +1,9 @@
import QtQuick
Rectangle {
property double diameter
implicitWidth: diameter
implicitHeight: diameter
radius: diameter / 2
}
@@ -197,16 +197,8 @@ Item { // Bar content region
implicitWidth: rightSectionRowLayout.implicitWidth
implicitHeight: Appearance.sizes.baseBarHeight
onScrollDown: {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume -= step;
}
onScrollUp: {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
}
onScrollDown: Audio.decrementVolume();
onScrollUp: Audio.incrementVolume();
onMovedAway: GlobalStates.osdVolumeOpen = false;
onPressed: event => {
if (event.button === Qt.LeftButton) {
@@ -1,3 +1,4 @@
import qs.modules.common.models.quickToggles
import qs.modules.common
import qs.modules.common.widgets
import qs.services
@@ -6,24 +7,7 @@ import Quickshell
AndroidQuickToggleButton {
id: root
property bool auto: Config.options.light.night.automatic
name: Translation.tr("Anti-flashbang")
toggled: Config.options.light.antiFlashbang.enable
buttonIcon: "flash_off"
mainAction: () => {
Config.options.light.antiFlashbang.enable = !Config.options.light.antiFlashbang.enable;
}
altAction: () => {
root.openMenu()
}
StyledToolTip {
text: Translation.tr("Anti-flashbang")
}
toggleModel: AntiFlashbangToggle {}
}
@@ -1,5 +1,6 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
@@ -8,19 +9,5 @@ import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Audio output")
statusText: toggled ? Translation.tr("Unmuted") : Translation.tr("Muted")
toggled: !Audio.sink?.audio?.muted
buttonIcon: Audio.sink?.audio?.muted ? "volume_off" : "volume_up"
mainAction: () => {
Audio.sink.audio.muted = !Audio.sink.audio.muted
}
altAction: () => {
root.openMenu()
}
StyledToolTip {
text: Translation.tr("Audio output | Right-click for volume mixer & device selector")
}
toggleModel: AudioToggle {}
}
@@ -1,5 +1,6 @@
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
@@ -9,22 +10,5 @@ import Quickshell.Bluetooth
AndroidQuickToggleButton {
id: root
name: Translation.tr("Bluetooth")
statusText: BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("No device")
available: BluetoothStatus.available
toggled: BluetoothStatus.enabled
buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
mainAction: () => {
Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter?.enabled
}
altAction: () => {
root.openMenu()
}
StyledToolTip {
text: Translation.tr("%1 | Right-click to configure").arg(
(BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("Bluetooth"))
+ (BluetoothStatus.activeDeviceCount > 1 ? ` +${BluetoothStatus.activeDeviceCount - 1}` : "")
)
}
toggleModel: BluetoothToggle {}
}
@@ -1,4 +1,5 @@
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
@@ -8,73 +9,5 @@ import Quickshell.Io
AndroidQuickToggleButton {
id: root
name: Translation.tr("Cloudflare WARP")
toggled: false
buttonIcon: "cloud_lock"
mainAction: () => {
if (toggled) {
root.toggled = false
Quickshell.execDetached(["warp-cli", "disconnect"])
} else {
root.toggled = true
Quickshell.execDetached(["warp-cli", "connect"])
}
}
Process {
id: connectProc
command: ["warp-cli", "connect"]
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
Quickshell.execDetached(["notify-send",
Translation.tr("Cloudflare WARP"),
Translation.tr("Connection failed. Please inspect manually with the <tt>warp-cli</tt> command")
, "-a", "Shell"
])
}
}
}
Process {
id: registrationProc
command: ["warp-cli", "registration", "new"]
onExited: (exitCode, exitStatus) => {
console.log("Warp registration exited with code and status:", exitCode, exitStatus)
if (exitCode === 0) {
connectProc.running = true
} else {
Quickshell.execDetached(["notify-send",
Translation.tr("Cloudflare WARP"),
Translation.tr("Registration failed. Please inspect manually with the <tt>warp-cli</tt> command"),
"-a", "Shell"
])
}
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", "warp-cli status"]
stdout: StdioCollector {
id: warpStatusCollector
onStreamFinished: {
if (warpStatusCollector.text.length > 0) {
root.visible = true
}
if (warpStatusCollector.text.includes("Unable")) {
registrationProc.running = true
} else if (warpStatusCollector.text.includes("Connected")) {
root.toggled = true
} else if (warpStatusCollector.text.includes("Disconnected")) {
root.toggled = false
}
}
}
}
StyledToolTip {
text: Translation.tr("Cloudflare WARP (1.1.1.1)")
}
toggleModel: CloudflareWarpToggle {}
}
@@ -1,5 +1,6 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
@@ -8,25 +9,5 @@ import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Color picker")
statusText: ""
toggled: false
buttonIcon: "colorize"
mainAction: () => {
GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start()
}
Timer {
id: delayedActionTimer
interval: 300
repeat: false
onTriggered: {
Quickshell.execDetached(["hyprpicker", "-a"])
}
}
StyledToolTip {
text: Translation.tr("Color picker")
}
toggleModel: ColorPickerToggle {}
}
@@ -1,27 +1,10 @@
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Dark Mode")
statusText: Appearance.m3colors.darkmode ? Translation.tr("Dark") : Translation.tr("Light")
toggled: Appearance.m3colors.darkmode
buttonIcon: "contrast"
mainAction: () => {
if (Appearance.m3colors.darkmode) {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
} else {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
}
}
StyledToolTip {
text: Translation.tr("Dark Mode")
}
toggleModel: DarkModeToggle {}
}
@@ -1,33 +1,11 @@
import qs
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("EasyEffects")
available: EasyEffects.available
toggled: EasyEffects.active
buttonIcon: "graphic_eq"
Component.onCompleted: {
EasyEffects.fetchActiveState()
}
mainAction: () => {
EasyEffects.toggle()
}
altAction: () => {
Quickshell.execDetached(["bash", "-c", "flatpak run com.github.wwmm.easyeffects || easyeffects"])
GlobalStates.sidebarRightOpen = false
}
StyledToolTip {
text: Translation.tr("EasyEffects | Right-click to configure")
}
toggleModel: EasyEffectsToggle {}
}
@@ -1,4 +1,5 @@
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
@@ -6,30 +7,5 @@ import Quickshell
import Quickshell.Io
AndroidQuickToggleButton {
id: root
name: Translation.tr("Game mode")
statusText: ""
toggled: toggled
buttonIcon: "gamepad"
mainAction: () => {
root.toggled = !root.toggled
if (root.toggled) {
Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`])
} else {
Quickshell.execDetached(["hyprctl", "reload"])
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`]
onExited: (exitCode, exitStatus) => {
root.toggled = exitCode !== 0 // Inverted because enabled = nonzero exit
}
}
StyledToolTip {
text: Translation.tr("Game mode")
}
toggleModel: GameModeToggle {}
}
@@ -1,21 +1,11 @@
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
AndroidQuickToggleButton {
id: root
name: Translation.tr("Keep awake")
toggled: Idle.inhibit
buttonIcon: "coffee"
mainAction: () => {
Idle.toggleInhibit()
}
StyledToolTip {
text: Translation.tr("Keep system awake")
}
toggleModel: IdleInhibitorToggle {}
}
@@ -1,26 +1,11 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Audio input")
statusText: toggled ? Translation.tr("Enabled") : Translation.tr("Muted")
toggled: !Audio.source?.audio?.muted
buttonIcon: Audio.source?.audio?.muted ? "mic_off" : "mic"
mainAction: () => {
Audio.source.audio.muted = !Audio.source.audio.muted
}
altAction: () => {
root.openMenu()
}
StyledToolTip {
text: Translation.tr("Audio input | Right-click for volume mixer & device selector")
}
toggleModel: MicToggle {}
}
@@ -1,5 +1,6 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import QtQuick
import Quickshell
@@ -8,25 +9,5 @@ import qs.services
AndroidQuickToggleButton {
id: root
toggled: SongRec.running
property bool sourceIsMonitor: SongRec.monitorSource === SongRec.MonitorSource.Monitor
name: Translation.tr("Identify Music")
statusText: toggled ? Translation.tr("Listening...") : sourceIsMonitor ? Translation.tr("System sound") : Translation.tr("Microphone")
buttonIcon: toggled ? "music_cast" : (sourceIsMonitor ? "music_note" : "frame_person_mic")
StyledToolTip {
text: Translation.tr("Recognize music | Right-click to toggle source")
}
mainAction: () => {
SongRec.toggleRunning()
}
altAction: () => {
SongRec.toggleMonitorSource()
}
toggleModel: MusicRecognitionToggle {}
}
@@ -1,5 +1,6 @@
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
@@ -7,17 +8,6 @@ import QtQuick
AndroidQuickToggleButton {
id: root
name: Translation.tr("Internet")
statusText: Network.networkName
toggled: Network.wifiStatus !== "disabled"
buttonIcon: Network.materialSymbol
mainAction: () => Network.toggleWifi()
altAction: () => {
root.openMenu()
}
StyledToolTip {
text: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
}
toggleModel: NetworkToggle {}
}
@@ -1,34 +1,11 @@
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
property bool auto: Config.options.light.night.automatic
name: Translation.tr("Night Light")
statusText: (auto ? Translation.tr("Auto, ") : "") + (toggled ? Translation.tr("Active") : Translation.tr("Inactive"))
toggled: Hyprsunset.active
buttonIcon: auto ? "night_sight_auto" : "bedtime"
mainAction: () => {
Hyprsunset.toggle()
}
altAction: () => {
root.openMenu()
}
Component.onCompleted: {
Hyprsunset.fetchState()
}
StyledToolTip {
text: Translation.tr("Night Light | Right-click to configure")
}
toggleModel: NightLightToggle {}
}
@@ -1,23 +1,11 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Notifications")
statusText: toggled ? Translation.tr("Show") : Translation.tr("Silent")
toggled: !Notifications.silent
buttonIcon: toggled ? "notifications_active" : "notifications_paused"
mainAction: () => {
Notifications.silent = !Notifications.silent;
}
StyledToolTip {
text: Translation.tr("Show notifications")
}
toggleModel: NotificationToggle {}
}
@@ -1,22 +1,11 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Virtual Keyboard")
toggled: GlobalStates.oskOpen
buttonIcon: toggled ? "keyboard_hide" : "keyboard"
mainAction: () => {
GlobalStates.oskOpen = !GlobalStates.oskOpen
}
StyledToolTip {
text: Translation.tr("On-screen keyboard")
}
toggleModel: OnScreenKeyboardToggle {}
}
@@ -1,5 +1,6 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
@@ -7,36 +8,5 @@ import Quickshell
import Quickshell.Services.UPower
AndroidQuickToggleButton {
id: root
name: Translation.tr("Power Profile")
toggled: PowerProfiles.profile !== PowerProfile.Balanced
buttonIcon: switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "energy_savings_leaf"
case PowerProfile.Balanced: return "settings_slow_motion"
case PowerProfile.Performance: return "local_fire_department"
}
statusText: switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "Power Saver"
case PowerProfile.Balanced: return "Balanced"
case PowerProfile.Performance: return "Performance"
}
mainAction: () => {
if (PowerProfiles.hasPerformanceProfile) {
switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: PowerProfiles.profile = PowerProfile.Balanced
break;
case PowerProfile.Balanced: PowerProfiles.profile = PowerProfile.Performance
break;
case PowerProfile.Performance: PowerProfiles.profile = PowerProfile.PowerSaver
break;
}
} else {
PowerProfiles.profile = PowerProfiles.profile == PowerProfile.Balanced ? PowerProfile.PowerSaver : PowerProfile.Balanced
}
}
StyledToolTip {
text: Translation.tr("Click to cycle through power profiles")
}
toggleModel: PowerProfilesToggle {}
}
@@ -2,29 +2,42 @@ import QtQuick
import QtQuick.Layouts
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.common.widgets
GroupButton {
id: root
// Info to be passed to by repeater
required property int buttonIndex
required property var buttonData
required property bool expandedSize
required property string buttonIcon
required property string name
required property var mainAction
property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive")
property bool available: true
required property real baseCellWidth
required property real baseCellHeight
required property real cellSpacing
required property int cellSize
// Signals
signal openMenu()
// Declared in specific toggles
property QuickToggleModel toggleModel
property string name: toggleModel?.name ?? ""
property string statusText: (toggleModel?.hasStatusText) ? (toggleModel?.statusText || (toggled ? Translation.tr("Active") : Translation.tr("Inactive"))) : ""
property string tooltipText: toggleModel?.tooltipText ?? ""
property string buttonIcon: toggleModel?.icon ?? "close"
property bool available: toggleModel?.available ?? true
toggled: toggleModel?.toggled ?? false
property var mainAction: toggleModel?.mainAction ?? null
altAction: toggleModel?.hasMenu ? (() => root.openMenu()) : (toggleModel?.altAction ?? null)
// Edit mode state
property bool editMode: false
// Sizing shenanigans
baseWidth: root.baseCellWidth * cellSize + cellSpacing * (cellSize - 1)
baseHeight: root.baseCellHeight
property bool editMode: false
enableImplicitWidthAnimation: !editMode && root.mouseArea.containsMouse
enableImplicitHeightAnimation: !editMode && root.mouseArea.containsMouse
Behavior on baseWidth {
@@ -41,8 +54,6 @@ GroupButton {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
signal openMenu()
enabled: available || editMode
padding: 6
horizontalPadding: padding
@@ -226,4 +237,9 @@ GroupButton {
event.accepted = true;
}
}
StyledToolTip {
extraVisibleCondition: root.tooltipText !== ""
text: root.tooltipText
}
}
@@ -1,5 +1,6 @@
import qs
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
@@ -7,27 +8,5 @@ import Quickshell
import Quickshell.Hyprland
AndroidQuickToggleButton {
id: root
name: Translation.tr("Screen snip")
statusText: ""
toggled: false
buttonIcon: "screenshot_region"
mainAction: () => {
GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start()
}
Timer {
id: delayedActionTimer
interval: 300
repeat: false
onTriggered: {
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
}
}
StyledToolTip {
text: Translation.tr("Screen snip")
}
toggleModel: ScreenSnipToggle {}
}
@@ -171,16 +171,8 @@ Item { // Bar content region
implicitWidth: Appearance.sizes.baseVerticalBarWidth
implicitHeight: bottomSectionColumnLayout.implicitHeight
onScrollDown: {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume -= step;
}
onScrollUp: {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
}
onScrollDown: Audio.decrementVolume();
onScrollUp: Audio.incrementVolume();
onMovedAway: GlobalStates.osdVolumeOpen = false;
onPressed: event => {
if (event.button === Qt.LeftButton) {
@@ -0,0 +1,113 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
Rectangle {
id: root
property int currentPage: 0
property alias columns: grid.columns
property alias rows: grid.rows
readonly property int itemsPerPage: columns * rows
property list<string> toggles: Config.options.waffles.actionCenter.toggles
property list<string> togglesInCurrentPage: toggles.slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage)
Layout.fillHeight: true
Layout.fillWidth: true
color: Looks.colors.bgPanelBody
implicitWidth: 360
implicitHeight: contentLayout.implicitHeight
ColumnLayout {
id: contentLayout
anchors.fill: parent
spacing: 0
Item {
id: togglesContainer
property real padding: 22
Layout.fillWidth: true
Layout.bottomMargin: -12
implicitHeight: grid.implicitHeight + padding * 2
GridLayout {
id: grid
anchors {
fill: parent
margins: parent.padding
}
columns: 3
rows: 2
rowSpacing: 12
columnSpacing: 12
uniformCellHeights: true
uniformCellWidths: true
Repeater {
model: ScriptModel {
values: root.togglesInCurrentPage
}
delegate: ActionCenterToggle {
required property var modelData
name: modelData
}
}
}
// TODO: pages indicator on the right
}
Rectangle {
implicitHeight: 1
Layout.fillWidth: true
color: Looks.colors.bg1Border
}
RowLayout {
Layout.margins: 12
Layout.topMargin: 18
Layout.bottomMargin: 14
spacing: 4
WPanelIconButton {
iconName: WIcons.volumeIcon
onClicked: {
Audio.sink.audio.muted = !Audio.sink.audio.muted;
}
}
WSlider {
Layout.fillWidth: true
value: Audio.sink.audio.volume
onMoved: {
Audio.sink.audio.volume = value;
}
}
WPanelIconButton {
contentItem: Item {
anchors.centerIn: parent
Row {
anchors.centerIn: parent
spacing: -1
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 18
icon: "settings"
}
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 12
icon: "chevron-right"
}
}
}
}
}
}
}
@@ -14,15 +14,9 @@ WBarAttachedPanelContent {
anchors.centerIn: parent
spacing: 0
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
ActionCenterBody {
topLeftRadius: root.border.radius - root.border.border.width
topRightRadius: topLeftRadius
color: Looks.colors.bgPanelBody
implicitWidth: 360
implicitHeight: 380
}
Rectangle {
@@ -32,46 +26,9 @@ WBarAttachedPanelContent {
implicitHeight: 1
}
Rectangle {
Layout.fillHeight: false
Layout.fillWidth: true
ActionCenterFooter {
bottomLeftRadius: root.border.radius - root.border.border.width
bottomRightRadius: bottomLeftRadius
color: Looks.colors.bgPanelFooter
implicitWidth: 360
implicitHeight: 47
// Battery button
WPanelFooterButton {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 12
contentItem: Row {
spacing: 4
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
icon: WIcons.batteryIcon
}
WText {
anchors.verticalCenter: parent.verticalCenter
text: `${Math.round(Battery.percentage * 100) || 0}%`
}
}
}
// Settings button
WPanelFooterButton {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
contentItem: FluentIcon {
icon: "settings"
}
}
}
}
}
@@ -0,0 +1,49 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
Rectangle {
Layout.fillHeight: false
Layout.fillWidth: true
color: Looks.colors.bgPanelFooter
implicitWidth: 360
implicitHeight: 47
// Battery button
WPanelFooterButton {
visible: Battery.available
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 12
contentItem: Row {
spacing: 4
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
icon: WIcons.batteryIcon
}
WText {
anchors.verticalCenter: parent.verticalCenter
text: `${Math.round(Battery.percentage * 100) || 0}%`
}
}
}
// Settings button
WPanelFooterButton {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
contentItem: FluentIcon {
icon: "settings"
}
}
}
@@ -0,0 +1,40 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
// It should be perfectly fine to use just a Column here, but somehow
// using ColumnLayout prevents weird opening anim stutter
ColumnLayout {
id: root
property alias name: toggleNameText.text
Rectangle {
Layout.fillWidth: true
implicitWidth: 96
implicitHeight: 48
color: "transparent"
border.width: 1
border.color: Looks.colors.bg0Border // ???
radius: Looks.radius.medium
}
Item {
implicitHeight: 36
Layout.fillWidth: true
WText {
id: toggleNameText
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
right: parent.right
}
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
text: "Toggle"
}
}
}
@@ -0,0 +1,16 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
import qs.modules.waffle.actionCenter
ActionCenterToggle {
id: root
name: Network.ethernet ? Translation.tr("Network") : Network.networkName
}
@@ -3,6 +3,7 @@ import QtQuick.Layouts
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.waffle.looks
BarButton {
@@ -38,22 +39,15 @@ BarButton {
id: volumeHoverArea
iconItem: FluentIcon {
anchors.verticalCenter: parent.verticalCenter
icon: {
const muted = Audio.sink?.audio.muted ?? false;
const volume = Audio.sink?.audio.volume ?? 0;
if (muted)
return volume > 0 ? "speaker-off" : "speaker-none";
if (volume == 0)
return "speaker-none";
if (volume < 0.5)
return "speaker-1";
return "speaker";
}
icon: WIcons.volumeIcon
}
onScrollDown: Audio.decrementVolume();
onScrollUp: Audio.incrementVolume();
}
IconHoverArea {
id: batteryHoverArea
visible: Battery?.available ?? false
iconItem: FluentIcon {
anchors.verticalCenter: parent.verticalCenter
icon: WIcons.batteryIcon
@@ -62,7 +56,7 @@ BarButton {
}
}
component IconHoverArea: MouseArea {
component IconHoverArea: FocusedScrollMouseArea {
id: hoverArea
required property var iconItem
anchors {
@@ -85,6 +85,7 @@ AppButton {
BarMenu {
id: contextMenu
noSmoothClosing: false // On the real thing this is always smooth
model: [
...((root.desktopEntry?.actions.length > 0) ? root.desktopEntry.actions.map(action =>({
@@ -35,6 +35,8 @@ Singleton {
property color bg2Border: root.dark ? "#464646" : "#EEEEEE"
property color fg: root.dark ? "#FFFFFF" : "#000000"
property color fg1: root.dark ? "#D1D1D1" : "#626262"
property color controlBg: root.dark ? "#9B9B9B" : "#868686"
property color controlFg: root.dark ? "#454545" : "#FFFFFF"
property color danger: "#C42B1C"
property color dangerActive: "#B62D1F"
property color warning: "#FF9900"
@@ -71,6 +73,9 @@ Singleton {
transition: QtObject {
id: transition
property int velocity: 850
property QtObject easing: QtObject {
property QtObject bezierCurve: QtObject {
readonly property list<real> easeInOut: [0.42,0.00,0.58,1.00,1,1]
@@ -27,5 +27,16 @@ Singleton {
if (Battery.percentage >= 0.9) return "battery-full";
return `battery-${Math.ceil(Battery.percentage * 10)}`;
}
property string volumeIcon: {
const muted = Audio.sink?.audio.muted ?? false;
const volume = Audio.sink?.audio.volume ?? 0;
if (muted)
return volume > 0 ? "speaker-off" : "speaker-none";
if (volume == 0)
return "speaker-none";
if (volume < 0.5)
return "speaker-1";
return "speaker";
}
}
@@ -0,0 +1,23 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
import qs.modules.waffle.bar
WButton {
id: root
property alias iconName: iconContent.icon
inset: 0
implicitWidth: 40
implicitHeight: 40
contentItem: FluentIcon {
id: iconContent
anchors.centerIn: parent
implicitSize: 18
icon: root.iconName
}
}
@@ -0,0 +1,81 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Widgets
import qs.modules.common
import qs.modules.common.widgets
Slider {
id: root
property real trackWidth: 4
// leftPadding: handle.width / 2
// rightPadding: handle.width / 2
leftPadding: 0
rightPadding: 0
implicitHeight: handle.implicitHeight
Behavior on value { // This makes the adjusted value (like volume) shift smoothly
SmoothedAnimation {
velocity: Looks.transition.velocity
}
}
background: Item {
id: background
anchors.fill: parent
Rectangle {
id: trackHighlight
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
topLeftRadius: root.trackWidth / 2
bottomLeftRadius: root.trackWidth / 2
color: Looks.colors.accent
implicitHeight: root.trackWidth
width: background.width * root.visualPosition
}
Rectangle {
id: trackTrough
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
topLeftRadius: root.trackWidth / 2
bottomLeftRadius: root.trackWidth / 2
color: Looks.colors.controlBg
implicitHeight: root.trackWidth
width: background.width * (1 - root.visualPosition)
}
}
handle: Circle {
id: handle
anchors.verticalCenter: parent.verticalCenter
x: (diameter / 2) + root.visualPosition * (root.width - diameter) - (diameter / 2)
diameter: 20
color: Looks.colors.controlFg
MouseArea {
id: handleMouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
}
Circle {
anchors.centerIn: parent
diameter: root.pressed ? 10 : handleMouseArea.containsMouse ? 14 : 12
color: Looks.colors.accent
Behavior on diameter {
animation: Looks.transition.enter.createObject(this)
}
}
}
}
+23 -1
View File
@@ -16,9 +16,31 @@ Singleton {
property PwNode source: Pipewire.defaultAudioSource
readonly property real hardMaxValue: 2.00 // People keep joking about setting volume to 5172% so...
property string audioTheme: Config.options.sounds.theme
property real value: sink?.audio.volume ?? 0
signal sinkProtectionTriggered(string reason);
function toggleMute() {
Audio.sink.audio.muted = !Audio.sink.audio.muted
}
function toggleMicMute() {
Audio.source.audio.muted = !Audio.source.audio.muted
}
function incrementVolume() {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
}
function decrementVolume() {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume -= step;
}
PwObjectTracker {
objects: [sink, source]
}
+1 -1
View File
@@ -90,7 +90,7 @@ ShellRoot {
property list<string> families: ["ii", "waffle"]
property var panelFamilies: ({
"ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"],
"waffle": ["wBar", "wBackground", "wActionCenter", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiSessionScreen", "iiSidebarRight", "iiWallpaperSelector"],
"waffle": ["wBar", "wBackground", "wActionCenter", "iiCheatsheet", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiSessionScreen", "iiSidebarRight", "iiWallpaperSelector"],
})
function cyclePanelFamily() {
const currentIndex = families.indexOf(Config.options.panelFamily)