diff options
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell')
13 files changed, 561 insertions, 39 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml index 3652af54..d52740b2 100644 --- a/accounts/gkleen@sif/shell/quickshell/Bar.qml +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml | |||
@@ -1,7 +1,6 @@ | |||
1 | import Quickshell | 1 | import Quickshell |
2 | import QtQuick | 2 | import QtQuick |
3 | 3 | ||
4 | |||
5 | PanelWindow { | 4 | PanelWindow { |
6 | id: bar | 5 | id: bar |
7 | 6 | ||
@@ -64,25 +63,39 @@ PanelWindow { | |||
64 | anchors.verticalCenter: parent.verticalCenter | 63 | anchors.verticalCenter: parent.verticalCenter |
65 | spacing: 0 | 64 | spacing: 0 |
66 | 65 | ||
67 | PipewireWidget {} | 66 | PrivacyWidget { |
67 | id: privacy | ||
68 | } | ||
68 | 69 | ||
69 | Item { | 70 | Item { |
71 | enabled: privacy.active | ||
70 | height: parent.height | 72 | height: parent.height |
71 | width: 4 | 73 | width: 8 |
72 | } | 74 | } |
73 | 75 | ||
74 | SystemTray {} | 76 | BatteryWidget {} |
77 | |||
78 | Item { | ||
79 | height: parent.height | ||
80 | width: 8 | ||
81 | } | ||
82 | |||
83 | BrightnessWidget {} | ||
75 | 84 | ||
76 | Item { | 85 | Item { |
77 | height: parent.height | 86 | height: parent.height |
78 | width: 4 | 87 | width: 4 |
79 | } | 88 | } |
80 | 89 | ||
90 | PipewireWidget {} | ||
91 | |||
92 | SystemTray {} | ||
93 | |||
81 | KeyboardLayout {} | 94 | KeyboardLayout {} |
82 | 95 | ||
83 | Item { | 96 | Item { |
84 | height: parent.height | 97 | height: parent.height |
85 | width: 4 | 98 | width: 8 - 4 |
86 | } | 99 | } |
87 | 100 | ||
88 | Clock {} | 101 | Clock {} |
diff --git a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml new file mode 100644 index 00000000..896440f1 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml | |||
@@ -0,0 +1,61 @@ | |||
1 | import QtQuick | ||
2 | import Quickshell | ||
3 | import Quickshell.Widgets | ||
4 | import Quickshell.Services.UPower | ||
5 | |||
6 | Item { | ||
7 | id: root | ||
8 | |||
9 | height: parent.height | ||
10 | width: batteryIcon.width | ||
11 | anchors.verticalCenter: parent.verticalCenter | ||
12 | |||
13 | property var batteryDevice: Array.from(UPower.devices.values).find(dev => dev.isLaptopBattery) | ||
14 | |||
15 | WrapperMouseArea { | ||
16 | id: widgetMouseArea | ||
17 | |||
18 | anchors.fill: parent | ||
19 | |||
20 | hoverEnabled: true | ||
21 | |||
22 | Item { | ||
23 | anchors.fill: parent | ||
24 | |||
25 | MaterialDesignIcon { | ||
26 | id: batteryIcon | ||
27 | |||
28 | implicitSize: 14 | ||
29 | anchors.centerIn: parent | ||
30 | |||
31 | icon: { | ||
32 | if (!root.batteryDevice?.ready) | ||
33 | return "battery-unknown"; | ||
34 | |||
35 | if (root.batteryDevice.state == UPowerDeviceState.FullyCharged) | ||
36 | return "power-plug-battery"; | ||
37 | |||
38 | const perdec = 10 * Math.max(1, Math.ceil(root.batteryDevice.percentage * 10)); | ||
39 | if (root.batteryDevice.state == UPowerDeviceState.Charging) | ||
40 | return `battery-charging-${perdec}`; | ||
41 | if (perdec == 100) | ||
42 | return "battery"; | ||
43 | return `battery-${perdec}`; | ||
44 | } | ||
45 | color: { | ||
46 | if (!root.batteryDevice?.ready) | ||
47 | return "#555"; | ||
48 | |||
49 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 20 * 60) | ||
50 | return "#f2201f"; | ||
51 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 40 * 60) | ||
52 | return "#f28a21"; | ||
53 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
54 | return "#fff"; | ||
55 | return "#555"; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml new file mode 100644 index 00000000..a432179e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml | |||
@@ -0,0 +1,117 @@ | |||
1 | import QtQuick | ||
2 | import QtQuick.Layouts | ||
3 | import Quickshell | ||
4 | import Quickshell.Widgets | ||
5 | import qs.Services | ||
6 | |||
7 | Scope { | ||
8 | id: root | ||
9 | |||
10 | property bool show: false | ||
11 | property bool inhibited: true | ||
12 | |||
13 | Connections { | ||
14 | target: Brightness | ||
15 | |||
16 | function onCurrBrightnessChanged() { | ||
17 | root.show = true; | ||
18 | hideTimer.restart(); | ||
19 | } | ||
20 | } | ||
21 | |||
22 | onShowChanged: { | ||
23 | if (show) | ||
24 | hideTimer.restart(); | ||
25 | } | ||
26 | |||
27 | Timer { | ||
28 | id: hideTimer | ||
29 | interval: 1000 | ||
30 | onTriggered: root.show = false | ||
31 | } | ||
32 | |||
33 | Timer { | ||
34 | id: startInhibit | ||
35 | interval: 100 | ||
36 | running: true | ||
37 | onTriggered: { | ||
38 | root.show = false; | ||
39 | root.inhibited = false; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | LazyLoader { | ||
44 | active: root.show && !root.inhibited | ||
45 | |||
46 | Variants { | ||
47 | model: Quickshell.screens | ||
48 | |||
49 | delegate: Scope { | ||
50 | id: screenScope | ||
51 | |||
52 | required property var modelData | ||
53 | |||
54 | PanelWindow { | ||
55 | id: window | ||
56 | |||
57 | screen: screenScope.modelData | ||
58 | |||
59 | anchors.top: true | ||
60 | margins.top: screen.height / 2 - 50 + 3.5 | ||
61 | exclusiveZone: 0 | ||
62 | exclusionMode: ExclusionMode.Ignore | ||
63 | |||
64 | implicitWidth: 400 | ||
65 | implicitHeight: 50 | ||
66 | |||
67 | mask: Region {} | ||
68 | |||
69 | color: "transparent" | ||
70 | |||
71 | Rectangle { | ||
72 | anchors.fill: parent | ||
73 | color: Qt.rgba(0, 0, 0, 0.75) | ||
74 | } | ||
75 | |||
76 | RowLayout { | ||
77 | id: layout | ||
78 | |||
79 | anchors.centerIn: parent | ||
80 | |||
81 | height: 50 - 8*2 | ||
82 | width: 400 - 8*2 | ||
83 | |||
84 | MaterialDesignIcon { | ||
85 | id: volumeIcon | ||
86 | |||
87 | implicitWidth: parent.height | ||
88 | implicitHeight: parent.height | ||
89 | |||
90 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
91 | } | ||
92 | |||
93 | Rectangle { | ||
94 | Layout.fillWidth: true | ||
95 | |||
96 | implicitHeight: 10 | ||
97 | |||
98 | color: "#50ffffff" | ||
99 | |||
100 | Rectangle { | ||
101 | anchors { | ||
102 | left: parent.left | ||
103 | top: parent.top | ||
104 | bottom: parent.bottom | ||
105 | } | ||
106 | |||
107 | color: "white" | ||
108 | |||
109 | implicitWidth: parent.width * Brightness.currBrightness | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml new file mode 100644 index 00000000..664b28e2 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml | |||
@@ -0,0 +1,33 @@ | |||
1 | import QtQuick | ||
2 | import Quickshell | ||
3 | import Quickshell.Widgets | ||
4 | import qs.Services | ||
5 | |||
6 | Item { | ||
7 | height: parent.height | ||
8 | width: brightnessIcon.width | ||
9 | anchors.verticalCenter: parent.verticalCenter | ||
10 | |||
11 | WrapperMouseArea { | ||
12 | id: widgetMouseArea | ||
13 | |||
14 | anchors.fill: parent | ||
15 | |||
16 | property real sensitivity: (1 / 50) / 120 | ||
17 | onWheel: event => Brightness.currBrightness += event.angleDelta.y * sensitivity | ||
18 | |||
19 | Item { | ||
20 | anchors.fill: parent | ||
21 | |||
22 | MaterialDesignIcon { | ||
23 | id: brightnessIcon | ||
24 | |||
25 | implicitSize: 14 | ||
26 | anchors.centerIn: parent | ||
27 | |||
28 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
29 | color: "#555" | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml index 387dcc8b..155a009e 100644 --- a/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml +++ b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml | |||
@@ -2,15 +2,26 @@ import QtQuick | |||
2 | import QtQuick.Effects | 2 | import QtQuick.Effects |
3 | 3 | ||
4 | Item { | 4 | Item { |
5 | id: icon | 5 | id: root |
6 | 6 | ||
7 | required property string icon | 7 | required property string icon |
8 | property color color: "white" | 8 | property color color: "white" |
9 | 9 | ||
10 | property real implicitSize: 0 | ||
11 | |||
12 | readonly property real actualSize: Math.min(root.width, root.height) | ||
13 | |||
14 | implicitWidth: root.implicitSize | ||
15 | implicitHeight: root.implicitSize | ||
16 | |||
10 | Image { | 17 | Image { |
11 | id: sourceImage | 18 | id: sourceImage |
12 | source: "file://" + @mdi@ + "/svg/" + icon.icon + ".svg" | 19 | source: "file://" + @mdi@ + "/svg/" + root.icon + ".svg" |
13 | anchors.fill: parent | 20 | anchors.fill: parent |
21 | fillMode: Image.PreserveAspectFit | ||
22 | |||
23 | sourceSize.width: root.actualSize | ||
24 | sourceSize.height: root.actualSize | ||
14 | 25 | ||
15 | layer.enabled: true | 26 | layer.enabled: true |
16 | layer.effect: MultiEffect { | 27 | layer.effect: MultiEffect { |
@@ -18,7 +29,7 @@ Item { | |||
18 | 29 | ||
19 | brightness: 1 | 30 | brightness: 1 |
20 | colorization: 1 | 31 | colorization: 1 |
21 | colorizationColor: icon.color | 32 | colorizationColor: root.color |
22 | } | 33 | } |
23 | } | 34 | } |
24 | } | 35 | } |
diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml index 5bf0ac3a..007ce100 100644 --- a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml | |||
@@ -27,7 +27,7 @@ Item { | |||
27 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | 27 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; |
28 | } | 28 | } |
29 | 29 | ||
30 | property real sensitivity: (1 / 20) / 120 | 30 | property real sensitivity: (1 / 40) / 120 |
31 | onWheel: event => { | 31 | onWheel: event => { |
32 | if (!Pipewire.defaultAudioSink) | 32 | if (!Pipewire.defaultAudioSink) |
33 | return; | 33 | return; |
@@ -50,8 +50,7 @@ Item { | |||
50 | MaterialDesignIcon { | 50 | MaterialDesignIcon { |
51 | id: volumeIcon | 51 | id: volumeIcon |
52 | 52 | ||
53 | width: 16 | 53 | implicitSize: 14 |
54 | height: 16 | ||
55 | anchors.centerIn: parent | 54 | anchors.centerIn: parent |
56 | 55 | ||
57 | icon: { | 56 | icon: { |
diff --git a/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml new file mode 100644 index 00000000..bb02528b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml | |||
@@ -0,0 +1,49 @@ | |||
1 | import QtQuick | ||
2 | import QtQuick.Layouts | ||
3 | import Quickshell | ||
4 | import Quickshell.Widgets | ||
5 | import qs.Services | ||
6 | |||
7 | Item { | ||
8 | height: parent.height | ||
9 | width: layout.childrenRect.width | ||
10 | anchors.verticalCenter: parent.verticalCenter | ||
11 | |||
12 | readonly property bool active: Boolean(Privacy.activeItems) | ||
13 | |||
14 | RowLayout { | ||
15 | id: layout | ||
16 | |||
17 | anchors.fill: parent | ||
18 | |||
19 | spacing: 8 | ||
20 | |||
21 | Repeater { | ||
22 | model: Privacy.activeItems | ||
23 | |||
24 | Item { | ||
25 | id: privacyItem | ||
26 | |||
27 | required property var modelData; | ||
28 | |||
29 | height: parent.height | ||
30 | width: icon.width | ||
31 | |||
32 | MaterialDesignIcon { | ||
33 | id: icon | ||
34 | |||
35 | implicitSize: 14 | ||
36 | anchors.centerIn: parent | ||
37 | |||
38 | icon: { | ||
39 | if (privacyItem.modelData == Privacy.Item.Microphone) | ||
40 | return "microphone"; | ||
41 | if (privacyItem.modelData == Privacy.Item.Screensharing) | ||
42 | return "monitor-share"; | ||
43 | } | ||
44 | color: "#f2201f" | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml new file mode 100644 index 00000000..87c7c05b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml | |||
@@ -0,0 +1,68 @@ | |||
1 | pragma Singleton | ||
2 | |||
3 | import QtQml | ||
4 | import Quickshell | ||
5 | import Quickshell.Io | ||
6 | import Custom as Custom | ||
7 | |||
8 | Singleton { | ||
9 | id: root | ||
10 | |||
11 | property string subsystem: "backlight" | ||
12 | property string device: "intel_backlight" | ||
13 | |||
14 | property real currBrightness | ||
15 | property real exponent: 4 | ||
16 | |||
17 | function calcCurrBrightness() { | ||
18 | if (!currFile.loaded || !maxFile.loaded) | ||
19 | return undefined; | ||
20 | const curr = Number(currFile.text()); | ||
21 | const max = Number(maxFile.text()); | ||
22 | const val = Math.pow(curr / max, 1 / root.exponent); | ||
23 | return val; | ||
24 | } | ||
25 | |||
26 | Component.onCompleted: root.currBrightness = root.calcCurrBrightness() | ||
27 | Connections { | ||
28 | target: currFile | ||
29 | function onLoaded() { root.currBrightness = root.calcCurrBrightness(); } | ||
30 | } | ||
31 | Connections { | ||
32 | target: maxFile | ||
33 | function onLoaded() { root.currBrightness = root.calcCurrBrightness(); } | ||
34 | } | ||
35 | |||
36 | onCurrBrightnessChanged: { | ||
37 | root.currBrightness = Math.max(0, Math.min(1, root.currBrightness)); | ||
38 | |||
39 | const prev = root.calcCurrBrightness(); | ||
40 | if (typeof prev === 'undefined' || Math.abs(root.currBrightness - prev) < 0.01) | ||
41 | return; | ||
42 | |||
43 | const max = Number(maxFile.text()); | ||
44 | const actual = Number(currFile.text()); | ||
45 | let curr = Math.max(0, Math.min(max, Math.pow(root.currBrightness, root.exponent) * max)); | ||
46 | if (Math.round(curr) == actual && curr < actual) | ||
47 | curr = Math.max(0, actual - 1); | ||
48 | else if (Math.round(curr) == actual && curr > actual) | ||
49 | curr = Math.min(max, actual + 1); | ||
50 | // root.currBrightness = Math.pow(curr / max, 1 / root.exponent); | ||
51 | Custom.Systemd.setBrightness(root.subsystem, root.device, Math.round(curr)); | ||
52 | } | ||
53 | |||
54 | FileView { | ||
55 | id: currFile | ||
56 | path: `/sys/class/${root.subsystem}/${root.device}/brightness` | ||
57 | blockAllReads: true | ||
58 | watchChanges: true | ||
59 | onFileChanged: reload() | ||
60 | } | ||
61 | FileView { | ||
62 | id: maxFile | ||
63 | path: `/sys/class/${root.subsystem}/${root.device}/max_brightness` | ||
64 | blockAllReads: true | ||
65 | watchChanges: true | ||
66 | onFileChanged: reload() | ||
67 | } | ||
68 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml new file mode 100644 index 00000000..9c813e49 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml | |||
@@ -0,0 +1,63 @@ | |||
1 | pragma Singleton | ||
2 | |||
3 | import QtQml | ||
4 | import Quickshell | ||
5 | import Quickshell.Services.Pipewire | ||
6 | |||
7 | Singleton { | ||
8 | id: root | ||
9 | |||
10 | PwObjectTracker { | ||
11 | objects: Pipewire.nodes.values | ||
12 | } | ||
13 | |||
14 | enum Item { | ||
15 | Microphone, | ||
16 | Screensharing | ||
17 | } | ||
18 | |||
19 | readonly property list<var> activeItems: { | ||
20 | var items = []; | ||
21 | if (microphoneActive) | ||
22 | items.push(Privacy.Item.Microphone); | ||
23 | if (screensharingActive) | ||
24 | items.push(Privacy.Item.Screensharing); | ||
25 | return items; | ||
26 | } | ||
27 | |||
28 | readonly property bool microphoneActive: { | ||
29 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
30 | return false | ||
31 | } | ||
32 | |||
33 | for (const node of Pipewire.nodes.values) { | ||
34 | if (!node || (node.type & PwNodeType.AudioInStream) != PwNodeType.AudioInStream) | ||
35 | continue; | ||
36 | |||
37 | if (node.properties?.["stream.monitor"] === "true") | ||
38 | continue; | ||
39 | |||
40 | if (node.audio?.muted) | ||
41 | continue; | ||
42 | |||
43 | return true; | ||
44 | } | ||
45 | |||
46 | return false; | ||
47 | } | ||
48 | |||
49 | readonly property bool screensharingActive: { | ||
50 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
51 | return false | ||
52 | } | ||
53 | |||
54 | for (const node of Pipewire.nodes.values) { | ||
55 | if (!node || (node.type & PwNodeType.VideoInStream) != PwNodeType.VideoInStream) | ||
56 | continue; | ||
57 | |||
58 | return true; | ||
59 | } | ||
60 | |||
61 | return false; | ||
62 | } | ||
63 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml index 55b1690e..6f70be29 100644 --- a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml +++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml | |||
@@ -47,7 +47,7 @@ Item { | |||
47 | return "" | 47 | return "" |
48 | } | 48 | } |
49 | 49 | ||
50 | width: 16 | 50 | width: icon.width + 6 |
51 | height: parent.height | 51 | height: parent.height |
52 | anchors.verticalCenter: parent.verticalCenter | 52 | anchors.verticalCenter: parent.verticalCenter |
53 | 53 | ||
@@ -83,14 +83,24 @@ Item { | |||
83 | } | 83 | } |
84 | } | 84 | } |
85 | 85 | ||
86 | IconImage { | 86 | Rectangle { |
87 | anchors.centerIn: parent | 87 | anchors.fill: parent |
88 | width: parent.width | 88 | color: { |
89 | height: parent.width | 89 | if (trayItemArea.containsMouse) |
90 | source: trayItemWrapper.iconSource | 90 | return "#33808080"; |
91 | asynchronous: true | 91 | return "transparent"; |
92 | smooth: true | 92 | } |
93 | mipmap: true | 93 | |
94 | IconImage { | ||
95 | id: icon | ||
96 | |||
97 | anchors.centerIn: parent | ||
98 | implicitSize: 16 | ||
99 | source: trayItemWrapper.iconSource | ||
100 | asynchronous: true | ||
101 | smooth: true | ||
102 | mipmap: true | ||
103 | } | ||
94 | } | 104 | } |
95 | } | 105 | } |
96 | 106 | ||
@@ -116,7 +126,7 @@ Item { | |||
116 | color: "black" | 126 | color: "black" |
117 | 127 | ||
118 | implicitWidth: Math.max(tooltipTitle.contentWidth, tooltipDescription.contentWidth) + 16 | 128 | implicitWidth: Math.max(tooltipTitle.contentWidth, tooltipDescription.contentWidth) + 16 |
119 | implicitHeight: tooltipTitle.contentHeight + tooltipDescription.contentHeight + 16 | 129 | implicitHeight: (trayItem.tooltipTitle ? tooltipTitle.contentHeight : 0) + (trayItem.tooltipDescription ? tooltipDescription.contentHeight : 0) + 16 |
120 | 130 | ||
121 | WrapperMouseArea { | 131 | WrapperMouseArea { |
122 | id: tooltipMouseArea | 132 | id: tooltipMouseArea |
@@ -130,6 +140,8 @@ Item { | |||
130 | Text { | 140 | Text { |
131 | id: tooltipTitle | 141 | id: tooltipTitle |
132 | 142 | ||
143 | enabled: trayItem.tooltipTitle | ||
144 | |||
133 | font.pointSize: 10 | 145 | font.pointSize: 10 |
134 | font.family: "Fira Sans" | 146 | font.family: "Fira Sans" |
135 | font.bold: true | 147 | font.bold: true |
@@ -141,6 +153,8 @@ Item { | |||
141 | Text { | 153 | Text { |
142 | id: tooltipDescription | 154 | id: tooltipDescription |
143 | 155 | ||
156 | enabled: trayItem.tooltipDescription | ||
157 | |||
144 | font.pointSize: 10 | 158 | font.pointSize: 10 |
145 | font.family: "Fira Sans" | 159 | font.family: "Fira Sans" |
146 | color: "white" | 160 | color: "white" |
diff --git a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml new file mode 100644 index 00000000..742ef4f5 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml | |||
@@ -0,0 +1,59 @@ | |||
1 | import Quickshell | ||
2 | import Quickshell.Io | ||
3 | import Quickshell.Services.Pipewire | ||
4 | import qs.Services | ||
5 | |||
6 | Scope { | ||
7 | id: root | ||
8 | |||
9 | SocketServer { | ||
10 | active: true | ||
11 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/shell.sock` | ||
12 | handler: Socket { | ||
13 | parser: SplitParser { | ||
14 | onRead: line => { | ||
15 | try { | ||
16 | const command = JSON.parse(line); | ||
17 | |||
18 | if (command.Volume) | ||
19 | root.onCommandVolume(command.Volume); | ||
20 | else if (command.Brightness) | ||
21 | root.onCommandBrightness(command.Brightness); | ||
22 | else | ||
23 | console.warn("UnixIPC: Command not handled:", JSON.stringify(command)); | ||
24 | } catch (e) { | ||
25 | console.warn("UnixIPC: Failed to parse command:", line, e); | ||
26 | } | ||
27 | } | ||
28 | } | ||
29 | |||
30 | onError: e => { | ||
31 | if (e == 1) | ||
32 | return; | ||
33 | console.warn("QLocalSocket::LocalSocketError", e); | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | PwObjectTracker { | ||
39 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
40 | } | ||
41 | function onCommandVolume(command) { | ||
42 | if (command.muted === "toggle") | ||
43 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
44 | if (command.volume === "up") | ||
45 | Pipewire.defaultAudioSink.audio.volume += 0.02; | ||
46 | if (command.volume === "down") | ||
47 | Pipewire.defaultAudioSink.audio.volume -= 0.02; | ||
48 | |||
49 | if (command["mic-muted"] === "toggle") | ||
50 | Pipewire.defaultAudioSource.audio.muted = !Pipewire.defaultAudioSource.audio.muted; | ||
51 | } | ||
52 | |||
53 | function onCommandBrightness(command) { | ||
54 | if (command === "up") | ||
55 | Brightness.currBrightness += 0.02 | ||
56 | if (command === "down") | ||
57 | Brightness.currBrightness -= 0.02 | ||
58 | } | ||
59 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml index 3a78d91b..16fb5dea 100644 --- a/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml +++ b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml | |||
@@ -7,23 +7,38 @@ import Quickshell.Widgets | |||
7 | Scope { | 7 | Scope { |
8 | id: root | 8 | id: root |
9 | 9 | ||
10 | property bool show: false | 10 | property string show: "" |
11 | property bool inhibited: true | 11 | property bool inhibited: true |
12 | 12 | ||
13 | PwObjectTracker { | 13 | PwObjectTracker { |
14 | objects: [ Pipewire.defaultAudioSink ] | 14 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] |
15 | } | 15 | } |
16 | 16 | ||
17 | Connections { | 17 | Connections { |
18 | enabled: Pipewire.defaultAudioSink | ||
18 | target: Pipewire.defaultAudioSink?.audio | 19 | target: Pipewire.defaultAudioSink?.audio |
19 | 20 | ||
20 | function onVolumeChanged() { | 21 | function onVolumeChanged() { |
21 | root.show = true; | 22 | root.show = "sink"; |
22 | hideTimer.reset(); | 23 | hideTimer.restart(); |
23 | } | 24 | } |
24 | function onMutedChanged() { | 25 | function onMutedChanged() { |
25 | root.show = true; | 26 | root.show = "sink"; |
26 | hideTimer.reset(); | 27 | hideTimer.restart(); |
28 | } | ||
29 | } | ||
30 | |||
31 | Connections { | ||
32 | enabled: Pipewire.defaultAudioSource | ||
33 | target: Pipewire.defaultAudioSource?.audio | ||
34 | |||
35 | function onVolumeChanged() { | ||
36 | root.show = "source"; | ||
37 | hideTimer.restart(); | ||
38 | } | ||
39 | function onMutedChanged() { | ||
40 | root.show = "source"; | ||
41 | hideTimer.restart(); | ||
27 | } | 42 | } |
28 | } | 43 | } |
29 | 44 | ||
@@ -34,15 +49,18 @@ Scope { | |||
34 | 49 | ||
35 | Timer { | 50 | Timer { |
36 | id: hideTimer | 51 | id: hideTimer |
37 | interval: 2000 | 52 | interval: 1000 |
38 | onTriggered: root.show = false | 53 | onTriggered: root.show = "" |
39 | } | 54 | } |
40 | 55 | ||
41 | Timer { | 56 | Timer { |
42 | id: startInhibit | 57 | id: startInhibit |
43 | interval: 100 | 58 | interval: 100 |
44 | running: true | 59 | running: true |
45 | onTriggered: root.inhibited = false; | 60 | onTriggered: { |
61 | root.show = ""; | ||
62 | root.inhibited = false; | ||
63 | } | ||
46 | } | 64 | } |
47 | 65 | ||
48 | LazyLoader { | 66 | LazyLoader { |
@@ -62,8 +80,9 @@ Scope { | |||
62 | screen: screenScope.modelData | 80 | screen: screenScope.modelData |
63 | 81 | ||
64 | anchors.top: true | 82 | anchors.top: true |
65 | margins.top: (screen.height - window.height) / 2 | 83 | margins.top: screen.height / 2 - 50 + 3.5 |
66 | exclusiveZone: 0 | 84 | exclusiveZone: 0 |
85 | exclusionMode: ExclusionMode.Ignore | ||
67 | 86 | ||
68 | implicitWidth: 400 | 87 | implicitWidth: 400 |
69 | implicitHeight: 50 | 88 | implicitHeight: 50 |
@@ -92,13 +111,20 @@ Scope { | |||
92 | implicitHeight: parent.height | 111 | implicitHeight: parent.height |
93 | 112 | ||
94 | icon: { | 113 | icon: { |
95 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | 114 | if (root.show == "sink") { |
96 | return "volume-off"; | 115 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) |
97 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | 116 | return "volume-off"; |
98 | return "volume-low"; | 117 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) |
99 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | 118 | return "volume-low"; |
100 | return "volume-medium"; | 119 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) |
101 | return "volume-high"; | 120 | return "volume-medium"; |
121 | return "volume-high"; | ||
122 | } else if (root.show == "source") { | ||
123 | if (!Pipewire.defaultAudioSource || Pipewire.defaultAudioSource.audio.muted) | ||
124 | return "microphone-off"; | ||
125 | return "microphone"; | ||
126 | } | ||
127 | return "volume-high"; | ||
102 | } | 128 | } |
103 | } | 129 | } |
104 | 130 | ||
@@ -118,7 +144,13 @@ Scope { | |||
118 | 144 | ||
119 | color: Pipewire.defaultAudioSink?.audio.muted ? "#70ffffff" : "white" | 145 | color: Pipewire.defaultAudioSink?.audio.muted ? "#70ffffff" : "white" |
120 | 146 | ||
121 | implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0) | 147 | implicitWidth: { |
148 | if (root.show == "sink") | ||
149 | return parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0); | ||
150 | else if (root.show == "source") | ||
151 | return parent.width * (Pipewire.defaultAudioSource?.audio.volume ?? 0); | ||
152 | return 0; | ||
153 | } | ||
122 | } | 154 | } |
123 | } | 155 | } |
124 | } | 156 | } |
diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml index 3657f77f..0fa45f79 100644 --- a/accounts/gkleen@sif/shell/quickshell/shell.qml +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml | |||
@@ -43,4 +43,7 @@ ShellRoot { | |||
43 | Lockscreen {} | 43 | Lockscreen {} |
44 | 44 | ||
45 | VolumeOSD {} | 45 | VolumeOSD {} |
46 | BrightnessOSD {} | ||
47 | |||
48 | UnixIPC {} | ||
46 | } | 49 | } |