From 5c8d8247499087a1c988f55f44c444520439118e Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Wed, 19 Nov 2025 23:39:18 +0100
Subject: [PATCH] waffles: action center: volume menu
---
.../assets/icons/fluent/arrow-left-filled.svg | 1 +
.../ii/assets/icons/fluent/arrow-left.svg | 1 +
.../icons/fluent/desktop-speaker-filled.svg | 1 +
.../assets/icons/fluent/desktop-speaker.svg | 1 +
.../assets/icons/fluent/headphones-filled.svg | 1 +
.../ii/assets/icons/fluent/headphones.svg | 1 +
.../icons/fluent/speaker-mute-filled.svg | 1 +
.../ii/assets/icons/fluent/speaker-mute.svg | 1 +
.../ii/modules/common/widgets/FadeLoader.qml | 2 +-
.../modules/common/widgets/PopupToolTip.qml | 8 +-
.../volumeMixer/VolumeDialogContent.qml | 21 +--
.../volumeMixer/VolumeMixerEntry.qml | 4 +-
.../actionCenter/ActionCenterContent.qml | 36 ++--
.../actionCenter/ActionCenterContext.qml | 13 ++
.../waffle/actionCenter/BodyRectangle.qml | 14 ++
.../waffle/actionCenter/FooterRectangle.qml | 17 ++
.../modules/waffle/actionCenter/HeaderRow.qml | 25 +++
.../waffle/actionCenter/PageColumn.qml | 6 +
.../waffle/actionCenter/SectionText.qml | 20 +++
.../modules/waffle/actionCenter/Separator.qml | 10 ++
.../actionCenter/WaffleActionCenter.qml | 3 +-
.../MainPageBody.qml} | 13 +-
.../MainPageBodySliders.qml} | 9 +
.../MainPageBodyToggles.qml} | 0
.../MainPageFooter.qml} | 9 +-
.../volumeControl/VolumeControl.qml | 160 ++++++++++++++++++
.../volumeControl/VolumeEntry.qml | 54 ++++++
.../ii/modules/waffle/bar/BarButton.qml | 28 +--
.../ii/modules/waffle/bar/TimeButton.qml | 1 +
.../ii/modules/waffle/bar/tray/Tray.qml | 4 +-
.../ii/modules/waffle/looks/Looks.qml | 3 +-
.../waffle/looks/WBarAttachedPanelContent.qml | 5 +-
.../ii/modules/waffle/looks/WButton.qml | 30 +++-
.../ii/modules/waffle/looks/WChoiceButton.qml | 67 ++++++++
.../ii/modules/waffle/looks/WFadeLoader.qml | 19 +++
.../ii/modules/waffle/looks/WIcons.qml | 69 ++++++--
.../modules/waffle/looks/WPanelIconButton.qml | 1 +
.../ii/modules/waffle/looks/WPopupToolTip.qml | 35 ++--
.../ii/modules/waffle/looks/WSlider.qml | 12 +-
.../ii/modules/waffle/looks/WToolTip.qml | 34 ++++
.../modules/waffle/looks/WToolTipContent.qml | 29 ++++
dots/.config/quickshell/ii/services/Audio.qml | 40 ++++-
42 files changed, 680 insertions(+), 129 deletions(-)
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-left-filled.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-left.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker-filled.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/headphones-filled.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/headphones.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute-filled.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute.svg
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/BodyRectangle.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/HeaderRow.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/PageColumn.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/SectionText.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/Separator.qml
rename dots/.config/quickshell/ii/modules/waffle/actionCenter/{ActionCenterBody.qml => mainPage/MainPageBody.qml} (77%)
rename dots/.config/quickshell/ii/modules/waffle/actionCenter/{ActionCenterBodySliders.qml => mainPage/MainPageBodySliders.qml} (87%)
rename dots/.config/quickshell/ii/modules/waffle/actionCenter/{ActionCenterBodyToggles.qml => mainPage/MainPageBodyToggles.qml} (100%)
rename dots/.config/quickshell/ii/modules/waffle/actionCenter/{ActionCenterFooter.qml => mainPage/MainPageFooter.qml} (88%)
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeEntry.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WFadeLoader.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-left-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-left-filled.svg
new file mode 100644
index 000000000..afb9f5a62
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-left-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-left.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-left.svg
new file mode 100644
index 000000000..38709f3e0
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker-filled.svg
new file mode 100644
index 000000000..509aedb83
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker.svg b/dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker.svg
new file mode 100644
index 000000000..32287733b
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/desktop-speaker.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/headphones-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/headphones-filled.svg
new file mode 100644
index 000000000..256823486
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/headphones-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/headphones.svg b/dots/.config/quickshell/ii/assets/icons/fluent/headphones.svg
new file mode 100644
index 000000000..d4ed6c490
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/headphones.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute-filled.svg
new file mode 100644
index 000000000..50c84c316
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute.svg b/dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute.svg
new file mode 100644
index 000000000..891d21e57
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/speaker-mute.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml
index f5064939b..f822792a3 100644
--- a/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml
+++ b/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml
@@ -1,12 +1,12 @@
import QtQuick
import qs.modules.common
-import qs.modules.common.widgets
Loader {
id: root
property bool shown: true
property alias fade: opacityBehavior.enabled
+ property alias animation: opacityBehavior.animation
opacity: shown ? 1 : 0
visible: opacity > 0
active: opacity > 0
diff --git a/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml b/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml
index afc9957a4..437c02a88 100644
--- a/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml
+++ b/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml
@@ -13,7 +13,9 @@ Item {
property bool alternativeVisibleCondition: false
property real horizontalPadding: 10
property real verticalPadding: 5
-
+ property real horizontalMargin: horizontalPadding
+ property real verticalMargin: verticalPadding
+
function updateAnchor() {
tooltipLoader.item?.anchor.updateAnchor();
}
@@ -49,8 +51,8 @@ Item {
}
color: "transparent"
- implicitWidth: root.contentItem.implicitWidth + root.horizontalPadding * 2
- implicitHeight: root.contentItem.implicitHeight + root.verticalPadding * 2
+ implicitWidth: root.contentItem.implicitWidth + root.horizontalMargin * 2
+ implicitHeight: root.contentItem.implicitHeight + root.verticalMargin * 2
data: [root.contentItem]
}
diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml
index 5eb409ecb..f7c2dc9f8 100644
--- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml
+++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml
@@ -10,15 +10,8 @@ import Quickshell.Services.Pipewire
ColumnLayout {
id: root
required property bool isSink
- function correctType(node) {
- return (node.isSink === root.isSink) && node.audio
- }
- readonly property list appPwNodes: Pipewire.nodes.values.filter((node) => { // Should be list but it breaks ScriptModel
- return root.correctType(node) && node.isStream
- })
- readonly property list devices: Pipewire.nodes.values.filter(node => {
- return root.correctType(node) && !node.isStream
- })
+ readonly property list appPwNodes: isSink ? Audio.outputAppNodes : Audio.inputAppNodes
+ readonly property list devices: isSink ? Audio.outputDevices : Audio.inputDevices
readonly property bool hasApps: appPwNodes.length > 0
spacing: 16
@@ -44,21 +37,21 @@ ColumnLayout {
Layout.fillHeight: false
Layout.fillWidth: true
Layout.bottomMargin: 6
- model: root.devices.map(node => (node.nickname || node.description || Translation.tr("Unknown")))
+ model: root.devices.map(node => Audio.friendlyDeviceName(node))
currentIndex: root.devices.findIndex(item => {
if (root.isSink) {
- return item.id === Pipewire.preferredDefaultAudioSink?.id
+ return item.id === Pipewire.defaultAudioSink?.id
} else {
- return item.id === Pipewire.preferredDefaultAudioSource?.id
+ return item.id === Pipewire.defaultAudioSource?.id
}
})
onActivated: (index) => {
print(index)
const item = root.devices[index]
if (root.isSink) {
- Pipewire.preferredDefaultAudioSink = item
+ Audio.setDefaultSink(item)
} else {
- Pipewire.preferredDefaultAudioSource = item
+ Audio.setDefaultSource(item)
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeMixerEntry.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeMixerEntry.qml
index a00335fb0..a871c05c6 100644
--- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeMixerEntry.qml
+++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeMixerEntry.qml
@@ -10,7 +10,7 @@ Item {
id: root
required property PwNode node
PwObjectTracker {
- objects: [node]
+ objects: [root.node]
}
implicitHeight: rowLayout.implicitHeight
@@ -47,7 +47,7 @@ Item {
elide: Text.ElideRight
text: {
// application.name -> description -> name
- const app = root.node?.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
+ const app = Audio.appNodeDisplayName(root.node);
const media = root.node.properties["media.name"];
return media != undefined ? `${app} • ${media}` : app;
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml
index 8519f5351..2b3385ece 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml
@@ -7,32 +7,34 @@ import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter.mainPage
WBarAttachedPanelContent {
id: root
- contentItem: StackView {
- implicitWidth: currentItem.implicitWidth
- implicitHeight: currentItem.implicitHeight
+ contentItem: StackView { // TODO: Make this a WStackView with proper anim
+ id: stackView
+ anchors.fill: parent
+ implicitWidth: initItem.implicitWidth
+ implicitHeight: initItem.implicitHeight
- initialItem: ColumnLayout {
- anchors.centerIn: parent
- spacing: 0
-
- ActionCenterBody {}
-
- Rectangle {
- Layout.fillHeight: false
- Layout.fillWidth: true
- color: Looks.colors.bgPanelSeparator
- implicitHeight: 1
- }
-
- ActionCenterFooter {}
+ initialItem: PageColumn {
+ id: initItem
+ MainPageBody {}
+ Separator {}
+ MainPageFooter {}
}
Component.onCompleted: {
ActionCenterContext.stackView = this
}
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.BackButton
+ onClicked: {
+ ActionCenterContext.back()
+ }
+ }
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContext.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContext.qml
index 7c75ba4c9..07aa54df9 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContext.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContext.qml
@@ -9,4 +9,17 @@ Singleton {
property StackView stackView
+ function push(component) {
+ if (stackView) {
+ item = stackView.push(component)
+ stackView.implicitWidth = item.implicitWidth
+ stackView.implicitHeight = item.implicitHeight
+ }
+ }
+
+ function back() {
+ if (stackView && stackView.depth > 1) {
+ stackView.pop()
+ }
+ }
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/BodyRectangle.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/BodyRectangle.qml
new file mode 100644
index 000000000..98e991359
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/BodyRectangle.qml
@@ -0,0 +1,14 @@
+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: true
+ Layout.fillWidth: true
+ color: Looks.colors.bgPanelBody
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml
new file mode 100644
index 000000000..f58ea6c9a
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml
@@ -0,0 +1,17 @@
+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
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/HeaderRow.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/HeaderRow.qml
new file mode 100644
index 000000000..a6458192b
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/HeaderRow.qml
@@ -0,0 +1,25 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
+
+RowLayout {
+ id: root
+
+ required property string title
+ spacing: 4
+
+ WPanelIconButton {
+ iconName: "arrow-left"
+ onClicked: ActionCenterContext.back()
+ }
+
+ WText {
+ id: titleText
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ text: root.title
+ font.pixelSize: Looks.font.pixelSize.large
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/PageColumn.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/PageColumn.qml
new file mode 100644
index 000000000..0ecb38f10
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/PageColumn.qml
@@ -0,0 +1,6 @@
+import QtQuick
+import QtQuick.Layouts
+
+ColumnLayout {
+ spacing: 0
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/SectionText.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/SectionText.qml
new file mode 100644
index 000000000..895ed4649
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/SectionText.qml
@@ -0,0 +1,20 @@
+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
+
+WText {
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+
+ font {
+ weight: Looks.font.weight.stronger
+ pixelSize: Looks.font.pixelSize.large
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/Separator.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/Separator.qml
new file mode 100644
index 000000000..17352a814
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/Separator.qml
@@ -0,0 +1,10 @@
+import QtQuick
+import QtQuick.Layouts
+import qs.modules.waffle.looks
+
+Rectangle {
+ Layout.fillHeight: false
+ Layout.fillWidth: true
+ color: Looks.colors.bgPanelSeparator
+ implicitHeight: 1
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml
index 565ac4ba2..9983cdb3b 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml
@@ -54,7 +54,8 @@ Scope {
ActionCenterContent {
id: content
- anchors.centerIn: parent
+ anchors.fill: parent
+ anchors.margins: visualMargin
focus: true
Keys.onPressed: event => { // Esc to close
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBody.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBody.qml
similarity index 77%
rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBody.qml
rename to dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBody.qml
index 0d4fd7183..80ec7de51 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBody.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBody.qml
@@ -6,15 +6,10 @@ import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
-Rectangle {
+BodyRectangle {
id: root
-
- Layout.fillHeight: true
- Layout.fillWidth: true
- color: Looks.colors.bgPanelBody
-
- implicitWidth: 360
implicitHeight: contentLayout.implicitHeight
ColumnLayout {
@@ -22,7 +17,7 @@ Rectangle {
anchors.fill: parent
spacing: 0
- ActionCenterBodyToggles {
+ MainPageBodyToggles {
id: togglesContainer
Layout.fillWidth: true
}
@@ -33,7 +28,7 @@ Rectangle {
color: Looks.colors.bg1Border
}
- ActionCenterBodySliders {
+ MainPageBodySliders {
Layout.margins: 12
Layout.topMargin: 18
Layout.bottomMargin: 14
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBodySliders.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodySliders.qml
similarity index 87%
rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBodySliders.qml
rename to dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodySliders.qml
index d42385ee6..506b828a0 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBodySliders.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodySliders.qml
@@ -6,6 +6,8 @@ import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
+import qs.modules.waffle.actionCenter.volumeControl
ColumnLayout {
id: root
@@ -58,6 +60,13 @@ ColumnLayout {
}
WPanelIconButton {
+ Component {
+ id: volumeControlComp
+ VolumeControl {}
+ }
+ onClicked: {
+ ActionCenterContext.push(volumeControlComp)
+ }
contentItem: Item {
anchors.centerIn: parent
Row {
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBodyToggles.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodyToggles.qml
similarity index 100%
rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterBodyToggles.qml
rename to dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodyToggles.qml
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterFooter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml
similarity index 88%
rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterFooter.qml
rename to dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml
index 2a03daa23..feeb744c3 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterFooter.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml
@@ -6,14 +6,9 @@ import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
-Rectangle {
- Layout.fillHeight: false
- Layout.fillWidth: true
- color: Looks.colors.bgPanelFooter
-
- implicitWidth: 360
- implicitHeight: 47
+FooterRectangle {
// Battery button
WPanelFooterButton {
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml
new file mode 100644
index 000000000..d4150ab7a
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml
@@ -0,0 +1,160 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import qs
+import qs.services
+import qs.modules.common
+import qs.modules.common.functions
+import qs.modules.common.widgets
+import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
+
+Rectangle {
+ id: root
+ implicitWidth: 360
+ implicitHeight: 352
+
+ PageColumn {
+ anchors.fill: parent
+
+ BodyRectangle {
+ implicitHeight: 400
+ implicitWidth: 50
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 4
+ spacing: 4
+
+ HeaderRow {
+ Layout.fillWidth: true
+ title: qsTr("Sound output")
+ }
+
+ StyledFlickable {
+ id: flickable
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ contentHeight: contentLayout.implicitHeight
+ contentWidth: width
+ clip: true
+
+ AudioChoices {
+ id: contentLayout
+ width: flickable.width
+ }
+ }
+ }
+ }
+
+ Separator {}
+
+ FooterRectangle {
+ WButton {
+ id: moreSettingsButton
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ }
+ inset: 0
+ implicitHeight: 40
+ implicitWidth: contentItem.implicitWidth + 30
+ color: "transparent"
+
+ onClicked: {
+ Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarLeft", "toggle"]);
+ Quickshell.execDetached(["bash", "-c", Config.options.apps.volumeMixer]);
+ }
+
+ contentItem: Item {
+ anchors.centerIn: parent
+ implicitWidth: buttonText.implicitWidth
+ WText {
+ id: buttonText
+ anchors.centerIn: parent
+ text: qsTr("More volume settings")
+ color: moreSettingsButton.pressed ? Looks.colors.fg : Looks.colors.fg1
+ }
+ }
+ }
+ }
+ }
+
+ component AudioChoices: ColumnLayout {
+ spacing: 4
+
+ SectionText {
+ text: qsTr("Output device")
+ }
+
+ Repeater {
+ model: ScriptModel {
+ values: Audio.outputDevices
+ }
+ delegate: WChoiceButton {
+ required property var modelData
+ icon.name: WIcons.audioDeviceIcon(modelData)
+ text: Audio.friendlyDeviceName(modelData)
+ checked: Audio.sink === modelData
+ onClicked: {
+ Audio.setDefaultSink(modelData);
+ }
+ }
+ }
+
+ Separator {
+ visible: EasyEffects.available
+ color: Looks.colors.bg2Hover
+ }
+
+ ////////////////////////////////////////////////////////////
+
+ SectionText {
+ visible: EasyEffects.available
+ text: qsTr("Sound effects")
+ }
+
+ WChoiceButton {
+ visible: EasyEffects.available
+ text: Translation.tr("Off")
+ checked: !EasyEffects.active
+ onClicked: EasyEffects.disable()
+ }
+
+ WChoiceButton {
+ visible: EasyEffects.available
+ text: "EasyEffects"
+ checked: EasyEffects.active
+ onClicked: EasyEffects.enable()
+ }
+
+ Separator {
+ color: Looks.colors.bg2Hover
+ }
+
+ ////////////////////////////////////////////////////////////
+
+ SectionText {
+ visible: EasyEffects.available
+ text: qsTr("Volume mixer")
+ }
+
+ VolumeEntry {
+ node: Audio.sink
+ icon: "speaker"
+ monochrome: true
+ }
+
+ Repeater {
+ model: ScriptModel {
+ values: Audio.outputAppNodes
+ }
+ delegate: VolumeEntry {
+ required property var modelData
+ node: modelData
+ }
+ }
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeEntry.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeEntry.qml
new file mode 100644
index 000000000..000aee2ac
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeEntry.qml
@@ -0,0 +1,54 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Services.Pipewire
+import qs
+import qs.services
+import qs.modules.common
+import qs.modules.common.functions
+import qs.modules.common.widgets
+import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
+
+RowLayout {
+ id: root
+ required property PwNode node
+ property alias icon: iconButton.iconName
+ property alias monochrome: iconButton.monochrome
+ monochrome: false
+
+ PwObjectTracker { // Necessary for useful info to be present in 'node'
+ objects: [root.node]
+ }
+
+ WPanelIconButton {
+ id: iconButton
+ iconName: WIcons.audioAppIcon(root.node)
+ onClicked: root.node.audio.muted = !root.node?.audio.muted
+
+ FluentIcon {
+ id: muteIcon
+ visible: root.node?.audio.muted ?? false
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
+ margins: -1
+ }
+ implicitSize: 14
+ icon: "speaker-mute"
+ }
+
+ WToolTip {
+ extraVisibleCondition: iconButton.shouldShowTooltip
+ text: Audio.appNodeDisplayName(root.node)
+ }
+ }
+
+ WSlider {
+ Layout.fillWidth: true
+ Layout.rightMargin: 10
+ value: root.node?.audio.volume ?? 0
+ onMoved: root.node.audio.volume = value
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml
index f8192389a..a2e745072 100644
--- a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml
@@ -5,41 +5,21 @@ import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
-Button {
+WButton {
id: root
property var altAction: () => {}
property var middleClickAction: () => {}
- property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
- property color colBackgroundHover: Looks.colors.bg1Hover
- property color colBackgroundActive: Looks.colors.bg1Active
+ colBackground: ColorUtils.transparentize(Looks.colors.bg1)
+ colBackgroundHover: Looks.colors.bg1Hover
+ colBackgroundActive: Looks.colors.bg1Active
property color colBackgroundBorder
property color color
Layout.fillHeight: true
topInset: 4
bottomInset: 4
- signal hoverTimedOut()
- property bool shouldShowTooltip: false
- property Timer hoverTimer: Timer {
- id: hoverTimer
- running: root.hovered
- interval: 400
- onTriggered: {
- root.hoverTimedOut()
- }
- }
- onHoverTimedOut: {
- root.shouldShowTooltip = true
- }
- onHoveredChanged: {
- if (!root.hovered) {
- root.shouldShowTooltip = false
- root.hoverTimer.stop()
- }
- }
-
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.contentTransparency : 1)
color: {
if (root.down) {
diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/TimeButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/TimeButton.qml
index 19da1ecdb..bf4c5c614 100644
--- a/dots/.config/quickshell/ii/modules/waffle/bar/TimeButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/bar/TimeButton.qml
@@ -37,6 +37,7 @@ BarButton {
}
}
FluentIcon {
+ visible: Notifications.silent
anchors.verticalCenter: parent.verticalCenter
icon: "alert-snooze"
implicitSize: 18
diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml
index f05f2593e..db021b032 100644
--- a/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml
@@ -105,8 +105,8 @@ RowLayout {
BarToolTip {
id: pinTooltip
extraVisibleCondition: trayButton.Drag.active && pinDropArea.containsDrag && pinDropArea.willPin
- realContentHorizontalPadding: 6
- realContentVerticalPadding: 6
+ horizontalPadding: 6
+ verticalPadding: 6
realContentItem: FluentIcon {
anchors.centerIn: parent
icon: "pin-off"
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
index c6488ab57..e62657636 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
@@ -76,7 +76,8 @@ Singleton {
}
property QtObject pixelSize: QtObject {
property real normal: 11
- property real large: 14
+ property real large: 13
+ property real larger: 15
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml
index ae984089d..9dfdc43f7 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml
@@ -78,11 +78,14 @@ Item {
}
}
- Item {
+ Rectangle {
id: contentArea
+ color: "red"
z: 0
anchors.fill: borderRect
anchors.margins: borderRect.border.width
+ implicitWidth: contentItem.implicitWidth
+ implicitHeight: contentItem.implicitHeight
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml
index b7a068698..d158c5949 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml
@@ -15,6 +15,7 @@ Button {
property color colBackgroundToggled: Looks.colors.accent
property color colBackgroundToggledHover: Looks.colors.accentHover
property color colBackgroundToggledActive: Looks.colors.accentActive
+ property color colForeground: Looks.colors.fg
property alias backgroundOpacity: backgroundRect.opacity
property color color: {
if (root.checked) {
@@ -35,7 +36,29 @@ Button {
}
}
+ // Hover stuff
+ signal hoverTimedOut()
+ property bool shouldShowTooltip: false
+ property Timer hoverTimer: Timer {
+ id: hoverTimer
+ running: root.hovered
+ interval: 400
+ onTriggered: {
+ root.hoverTimedOut()
+ }
+ }
+ onHoverTimedOut: {
+ root.shouldShowTooltip = true
+ }
+ onHoveredChanged: {
+ if (!root.hovered) {
+ root.shouldShowTooltip = false
+ root.hoverTimer.stop()
+ }
+ }
+
property alias monochromeIcon: buttonIcon.monochrome
+ property alias buttonSpacing: contentLayout.spacing
property bool forceShowIcon: false
property var altAction: () => {}
@@ -93,16 +116,16 @@ Button {
spacing: 12
FluentIcon {
id: buttonIcon
- visible: root.icon.name !== "" || root.forceShowIcon
monochrome: true
implicitSize: 16
- Layout.leftMargin: 6
+ Layout.leftMargin: root.iconLeftMargin
Layout.fillWidth: false
Layout.alignment: Qt.AlignVCenter
icon: root.icon.name
+ color: root.colForeground
+ visible: root.icon.name !== ""
}
WText {
- Layout.rightMargin: 12
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
text: root.text
@@ -110,6 +133,7 @@ Button {
font {
pixelSize: Looks.font.pixelSize.large
}
+ color: root.colForeground
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml
new file mode 100644
index 000000000..60de3d816
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml
@@ -0,0 +1,67 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import qs
+import qs.services
+import qs.modules.common
+import qs.modules.common.functions
+import qs.modules.common.widgets
+import qs.modules.waffle.looks
+
+WButton {
+ id: root
+
+ Layout.fillWidth: true
+ implicitWidth: contentItem.implicitWidth
+ horizontalPadding: 10
+ verticalPadding: 11
+ inset: 0
+ buttonSpacing: 8
+
+ property color color: {
+ if (root.checked) {
+ if (root.down) {
+ return root.colBackgroundHover;
+ } else if (root.hovered && !root.down) {
+ return root.colBackgroundActive;
+ } else {
+ return root.colBackgroundHover;
+ }
+ }
+ if (root.down) {
+ return root.colBackgroundActive;
+ } else if (root.hovered && !root.down) {
+ return root.colBackgroundHover;
+ } else {
+ return root.colBackground;
+ }
+ }
+
+ background: Rectangle {
+ id: backgroundRect
+ radius: Looks.radius.medium
+ color: root.color
+ Behavior on color {
+ animation: Looks.transition.color.createObject(this)
+ }
+
+ WFadeLoader {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ shown: root.checked
+ sourceComponent: Rectangle {
+ implicitWidth: 3
+ implicitHeight: 3
+ radius: width / 2
+ color: Looks.colors.accent
+ Component.onCompleted: {
+ implicitHeight = 16;
+ }
+
+ Behavior on implicitHeight {
+ animation: Looks.transition.opacity.createObject(this)
+ }
+ }
+ }
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WFadeLoader.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WFadeLoader.qml
new file mode 100644
index 000000000..c815ec0b8
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WFadeLoader.qml
@@ -0,0 +1,19 @@
+import QtQuick
+import qs.modules.common
+
+// Yes, this is (mostly) a copy of FadeLoader.
+// The animation of a Behavior cannot be changed... I'd love to be proven wrong.
+Loader {
+ id: root
+ property bool shown: true
+ property alias fade: opacityBehavior.enabled
+ property alias animation: opacityBehavior.animation
+ opacity: shown ? 1 : 0
+ visible: opacity > 0
+ active: opacity > 0
+
+ Behavior on opacity {
+ id: opacityBehavior
+ animation: Looks.transition.opacity.createObject(null)
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml
index c0fd7381d..42836fbc7 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml
@@ -8,24 +8,34 @@ Singleton {
id: root
property string internetIcon: {
- if (Network.ethernet) return "ethernet";
+ if (Network.ethernet)
+ return "ethernet";
if (Network.wifiEnabled) {
const strength = Network.networkStrength;
- if (strength > 75) return "wifi-1";
- if (strength > 50) return "wifi-2";
- if (strength > 25) return "wifi-3";
+ if (strength > 75)
+ return "wifi-1";
+ if (strength > 50)
+ return "wifi-2";
+ if (strength > 25)
+ return "wifi-3";
return "wifi-4";
}
- if (Network.wifiStatus === "connecting") return "wifi-4";
- if (Network.wifiStatus === "disconnected") return "wifi-off";
- if (Network.wifiStatus === "disabled") return "wifi-off";
+ if (Network.wifiStatus === "connecting")
+ return "wifi-4";
+ if (Network.wifiStatus === "disconnected")
+ return "wifi-off";
+ if (Network.wifiStatus === "disabled")
+ return "wifi-off";
return "wifi-warning";
}
property string batteryIcon: {
- if (Battery.isCharging) return "battery-charge";
- if (Battery.isCriticalAndNotCharging) return "battery-warning";
- if (Battery.percentage >= 0.9) return "battery-full";
+ if (Battery.isCharging)
+ return "battery-charge";
+ if (Battery.isCriticalAndNotCharging)
+ return "battery-warning";
+ if (Battery.percentage >= 0.9)
+ return "battery-full";
return `battery-${Math.ceil(Battery.percentage * 10)}`;
}
@@ -33,7 +43,7 @@ Singleton {
const muted = Audio.sink?.audio.muted ?? false;
const volume = Audio.sink?.audio.volume ?? 0;
if (muted)
- return volume > 0 ? "speaker-off" : "speaker-none";
+ return "speaker-mute";
if (volume == 0)
return "speaker-none";
if (volume < 0.5)
@@ -53,10 +63,39 @@ Singleton {
property string notificationsIcon: Notifications.silent ? "alert-snooze" : "alert"
property string powerProfileIcon: {
- switch(PowerProfiles.profile) {
- case PowerProfile.PowerSaver: return "leaf-two";
- case PowerProfile.Balanced: return "flash-on";
- case PowerProfile.Performance: return "fire";
+ switch (PowerProfiles.profile) {
+ case PowerProfile.PowerSaver:
+ return "leaf-two";
+ case PowerProfile.Balanced:
+ return "flash-on";
+ case PowerProfile.Performance:
+ return "fire";
}
}
+
+ function audioDeviceIcon(node) {
+ if (!node.isSink)
+ return "mic-on";
+ const monitor = /monitor|hdmi/i;
+ const headphones = /headset|headphone|bluez|wireless/i;
+ const speakers = /speaker|output/i;
+ if (monitor.test(node.nickname) || monitor.test(node.description) || monitor.test(node.name)) {
+ return "desktop-speaker";
+ }
+ if (headphones.test(node.nickname) || headphones.test(node.description) || headphones.test(node.name)) {
+ return "headphones";
+ }
+ if (speakers.test(node.nickname) || speakers.test(node.description) || speakers.test(node.name)) {
+ return "speaker";
+ }
+ return "speaker";
+ }
+
+ function audioAppIcon(node) {
+ let icon;
+ icon = AppSearch.guessIcon(node?.properties["application.icon-name"] ?? "");
+ if (AppSearch.iconExists(icon)) return icon;
+ icon = AppSearch.guessIcon(node?.properties["node.name"] ?? "");
+ return icon;
+ }
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml
index edaad11a6..8d11ec199 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml
@@ -10,6 +10,7 @@ WButton {
id: root
property alias iconName: iconContent.icon
+ property alias monochrome: iconContent.monochrome
inset: 0
implicitWidth: 40
implicitHeight: 40
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml
index feddc3793..d98572261 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml
@@ -10,37 +10,22 @@ import qs.modules.waffle.looks
PopupToolTip {
id: root
- property Item realContentItem
+ required property Item realContentItem
realContentItem: WText {
text: root.text
anchors.centerIn: parent
}
property real visualMargin: 11
- verticalPadding: visualMargin
- horizontalPadding: visualMargin
- property real realContentVerticalPadding: 8
- property real realContentHorizontalPadding: 10
+ verticalPadding: 8
+ horizontalPadding: 10
+ verticalMargin: visualMargin
+ horizontalMargin: visualMargin
- contentItem: Item {
- anchors.centerIn: parent
- implicitWidth: realContent.implicitWidth + 2 * 2
- implicitHeight: realContent.implicitHeight + 2 * 2
-
- WAmbientShadow {
- target: realContent
- }
-
- Rectangle {
- id: realContent
- z: 1
- anchors.centerIn: parent
- implicitWidth: root.realContentItem.implicitWidth + root.realContentHorizontalPadding * 2
- implicitHeight: root.realContentItem.implicitHeight + root.realContentVerticalPadding * 2
- color: Looks.colors.bg1
- radius: Looks.radius.medium
-
- children: [root.realContentItem]
- }
+ contentItem: WToolTipContent {
+ id: tooltipContent
+ realContentItem: root.realContentItem
+ horizontalPadding: root.horizontalPadding
+ verticalPadding: root.verticalPadding
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WSlider.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WSlider.qml
index 30086f255..8c3c51e03 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WSlider.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WSlider.qml
@@ -10,8 +10,7 @@ Slider {
id: root
property real trackWidth: 4
- // leftPadding: handle.width / 2
- // rightPadding: handle.width / 2
+ property string tooltipContent: `${Math.round(value * 100)}`
leftPadding: 0
rightPadding: 0
@@ -77,5 +76,14 @@ Slider {
animation: Looks.transition.enter.createObject(this)
}
}
+
+ WToolTip {
+ id: tooltip
+ extraVisibleCondition: root.pressed
+ text: root.tooltipContent
+ font.pixelSize: Looks.font.pixelSize.larger
+ verticalPadding: 3
+ horizontalPadding: 8
+ }
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml
new file mode 100644
index 000000000..3c8d20d26
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml
@@ -0,0 +1,34 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import qs.modules.common
+import qs.modules.common.functions
+import qs.modules.common.widgets
+import qs.modules.waffle.looks
+
+StyledToolTip {
+ id: root
+
+ required property Item realContentItem
+ font {
+ family: Looks.font.family.ui
+ pixelSize: Looks.font.pixelSize.normal
+ weight: Looks.font.weight.regular
+ }
+ realContentItem: WText {
+ text: root.text
+ font: root.font
+ anchors.centerIn: parent
+ }
+
+ verticalPadding: 8
+ horizontalPadding: 10
+
+ contentItem: WToolTipContent {
+ id: tooltipContent
+ realContentItem: root.realContentItem
+ horizontalPadding: root.horizontalPadding
+ verticalPadding: root.verticalPadding
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml
new file mode 100644
index 000000000..57396fef5
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml
@@ -0,0 +1,29 @@
+import QtQuick
+import Quickshell
+import qs.modules.waffle.looks
+
+Item {
+ id: root
+ anchors.centerIn: parent
+ required property Item realContentItem
+ property real verticalPadding: 8
+ property real horizontalPadding: 10
+ implicitWidth: realContent.implicitWidth + 2 * 2
+ implicitHeight: realContent.implicitHeight + 2 * 2
+
+ WAmbientShadow {
+ target: realContent
+ }
+
+ Rectangle {
+ id: realContent
+ z: 1
+ anchors.centerIn: parent
+ implicitWidth: root.realContentItem.implicitWidth + root.horizontalPadding * 2
+ implicitHeight: root.realContentItem.implicitHeight + root.verticalPadding * 2
+ color: Looks.colors.bg1
+ radius: Looks.radius.medium
+
+ children: [root.realContentItem]
+ }
+}
diff --git a/dots/.config/quickshell/ii/services/Audio.qml b/dots/.config/quickshell/ii/services/Audio.qml
index 633890cd0..4b45701a9 100644
--- a/dots/.config/quickshell/ii/services/Audio.qml
+++ b/dots/.config/quickshell/ii/services/Audio.qml
@@ -11,15 +11,43 @@ import Quickshell.Services.Pipewire
Singleton {
id: root
+ // Misc props
property bool ready: Pipewire.defaultAudioSink?.ready ?? false
property PwNode sink: Pipewire.defaultAudioSink
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
-
+ function friendlyDeviceName(node) {
+ return (node.nickname || node.description || Translation.tr("Unknown"));
+ }
+ function appNodeDisplayName(node) {
+ return (node.properties["application.name"] || node.description || node.name)
+ }
+
+ // Lists
+ function correctType(node, isSink) {
+ return (node.isSink === isSink) && node.audio
+ }
+ function appNodes(isSink) {
+ return Pipewire.nodes.values.filter((node) => { // Should be list but it breaks ScriptModel
+ return root.correctType(node, isSink) && node.isStream
+ })
+ }
+ function devices(isSink) {
+ return Pipewire.nodes.values.filter(node => {
+ return root.correctType(node, isSink) && !node.isStream
+ })
+ }
+ readonly property list outputAppNodes: root.appNodes(true)
+ readonly property list inputAppNodes: root.appNodes(false)
+ readonly property list outputDevices: root.devices(true)
+ readonly property list inputDevices: root.devices(false)
+
+ // Signals
signal sinkProtectionTriggered(string reason);
+ // Controls
function toggleMute() {
Audio.sink.audio.muted = !Audio.sink.audio.muted
}
@@ -39,8 +67,16 @@ Singleton {
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume -= step;
}
-
+ function setDefaultSink(node) {
+ Pipewire.preferredDefaultAudioSink = node;
+ }
+
+ function setDefaultSource(node) {
+ Pipewire.preferredDefaultAudioSource = node;
+ }
+
+ // Internals
PwObjectTracker {
objects: [sink, source]
}