summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Bar.qml4
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/Worktime.qml75
-rw-r--r--accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml193
-rw-r--r--home-modules/quickshell.nix3
4 files changed, 220 insertions, 55 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml
index c998c335..c0ff4dac 100644
--- a/accounts/gkleen@sif/shell/quickshell/Bar.qml
+++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml
@@ -66,9 +66,7 @@ PanelWindow {
66 anchors.verticalCenter: parent.verticalCenter 66 anchors.verticalCenter: parent.verticalCenter
67 spacing: 0 67 spacing: 0
68 68
69 // WorktimeWidget { command: "time"; } 69 WorktimeWidget {}
70
71 // WorktimeWidget { command: "today"; }
72 70
73 KeyboardLayout {} 71 KeyboardLayout {}
74 72
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Worktime.qml b/accounts/gkleen@sif/shell/quickshell/Services/Worktime.qml
new file mode 100644
index 00000000..fdb45aa0
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/Worktime.qml
@@ -0,0 +1,75 @@
1pragma Singleton
2
3import QtQuick
4import Quickshell
5import Quickshell.Io
6import QtQml
7
8Singleton {
9 id: root
10
11 property alias time: timeState
12 property alias today: todayState
13
14 CommandState {
15 id: timeState
16 command: "time"
17 }
18 CommandState {
19 id: todayState
20 command: "today"
21 }
22
23 component CommandState : Scope {
24 id: commandState
25
26 required property string command
27 property var state: null
28
29 property bool strikeout: !strikeoutTimer.running
30 property alias running: process.running
31 property alias updating: updateTimer.running
32
33 Process {
34 id: process
35 running: true
36 command: [ @worktime@, commandState.command, "--waybar" ]
37 stdout: StdioCollector {
38 id: processCollector
39 onStreamFinished: {
40 try {
41 commandState.state = JSON.parse(processCollector.text);
42 strikeoutTimer.restart();
43 } catch (e) {
44 console.warn("Worktime: Failed to parse output:", processCollector.text, e);
45 }
46 }
47 }
48 }
49
50 Timer {
51 id: updateTimer
52 running: commandState.state?.class == "running" || commandState.state?.class == "over"
53 interval: 60000
54 repeat: true
55 onTriggered: process.running = true
56 }
57
58 Timer {
59 id: strikeoutTimer
60 running: false
61 interval: 5 * updateTimer.interval
62 repeat: false
63 }
64 }
65
66 Timer {
67 running: Boolean(timeState.state) && Boolean(todayState.state) && timeState.strikeout && todayState.strikeout
68 interval: 1000
69 repeat: false
70 onTriggered: {
71 timeState.state = null;
72 todayState.state = null;
73 }
74 }
75}
diff --git a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml
index 04bcc581..0e07ff59 100644
--- a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml
+++ b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml
@@ -1,81 +1,125 @@
1import QtQml 1import QtQml
2import Quickshell 2import Quickshell
3import Quickshell.Io
4import QtQuick 3import QtQuick
5import Quickshell.Widgets 4import Quickshell.Widgets
5import qs.Services
6 6
7Item { 7Item {
8 id: root 8 id: root
9 9
10 required property string command
11 property var state: null
12
13 height: parent.height 10 height: parent.height
14 width: label.contentWidth + 8 11 width: (timeWidget.visible ? timeWidget.label.contentWidth + 8 : 0) + (todayWidget.visible ? todayWidget.label.contentWidth + 8 : 0) + (icon.visible ? icon.implicitWidth + 8 : 0)
15 anchors.verticalCenter: parent.verticalCenter 12 anchors.verticalCenter: parent.verticalCenter
16 13
17 Process { 14 component TextWidget : Item {
18 id: process 15 id: textWidget
19 running: true 16
20 command: [ @worktime@, root.command, "--waybar" ] 17 visible: textWidget.state.state?.text ?? false
21 stdout: StdioCollector { 18
22 id: processCollector 19 required property var state
23 onStreamFinished: { 20 property alias label: label
24 try { 21 property alias mouseArea: mouseArea
25 root.state = JSON.parse(processCollector.text); 22
26 } catch (e) { 23 anchors.verticalCenter: parent.verticalCenter
27 console.warn("Worktime: Failed to parse output:", processCollector.text, e); 24 implicitWidth: label.contentWidth + 8
25 height: parent.height
26
27 WrapperMouseArea {
28 id: mouseArea
29
30 anchors.fill: parent
31
32 enabled: true
33 hoverEnabled: true
34 acceptedButtons: Qt.NoButton
35 cursorShape: Qt.PointingHandCursor
36
37 Item {
38 anchors.fill: parent
39
40 Text {
41 id: label
42
43 anchors.centerIn: parent
44
45 text: textWidget.state.state?.text ?? ""
46
47 font.pointSize: 10
48 font.family: "Fira Sans"
49 font.strikeout: textWidget.state.strikeout
50 color: {
51 if (textWidget.state.state?.class == "running")
52 return "white";
53 if (textWidget.state.state?.class == "over")
54 return "#f28a21";
55 return "#555";
56 }
28 } 57 }
29 } 58 }
30 } 59 }
31 } 60 }
32 61
33 Timer {
34 running: true
35 interval: 60
36 repeat: true
37 onTriggered: process.running = true
38 }
39
40 WrapperMouseArea { 62 WrapperMouseArea {
41 id: mouseArea 63 id: mouseArea
42 64
43 anchors.fill: parent 65 anchors.fill: parent
44
45 enabled: true
46 hoverEnabled: true 66 hoverEnabled: true
47 67
48 Item { 68 cursorShape: Qt.PointingHandCursor
49 anchors.fill: parent 69 onClicked: {
70 Worktime.time.running = true;
71 Worktime.today.running = true;
72 }
50 73
51 Text { 74 Rectangle {
52 id: label 75 anchors.fill: parent
76 color: {
77 if (mouseArea.containsMouse)
78 return "#33808080";
79 return "transparent";
80 }
53 81
82 Row {
83 height: parent.height
54 anchors.centerIn: parent 84 anchors.centerIn: parent
85 spacing: 0
86
87 TextWidget {
88 id: timeWidget
89 state: Worktime.time
90 }
91
92 TextWidget {
93 id: todayWidget
94 state: Worktime.today
95 }
96
97 MaterialDesignIcon {
98 id: icon
99
100 anchors.verticalCenter: parent.verticalCenter
55 101
56 visible: root.state?.text ?? false 102 implicitSize: 14
57 text: root.state?.text ?? "" 103
58 104 visible: !timeWidget.visible && !todayWidget.visible
59 font.pointSize: 10 105
60 font.family: "Fira Sans" 106 icon: (Worktime.time.running || Worktime.today.running) ? "update" : "timer-off"
61 color: { 107 color: "#555"
62 if (root.state?.class == "running")
63 return "white";
64 if (root.state?.class == "over")
65 return "#f28a21";
66 return "#555";
67 } 108 }
68 } 109 }
69 } 110 }
70 } 111 }
71 112
72 PopupWindow { 113 component WorktimePopup : PopupWindow {
73 id: tooltip 114 id: tooltip
74 115
75 property bool nextVisible: Boolean(root.state?.tooltip ?? false) && (mouseArea.containsMouse || tooltipMouseArea.containsMouse) 116 required property var state
117 required property var mouseArea
118
119 property bool nextVisible: (tooltipText.visible || tooltipIcon.visible) && (tooltip.mouseArea.containsMouse || tooltipMouseArea.containsMouse)
76 120
77 anchor { 121 anchor {
78 item: mouseArea 122 item: tooltip.mouseArea
79 edges: Edges.Bottom | Edges.Left 123 edges: Edges.Bottom | Edges.Left
80 } 124 }
81 visible: false 125 visible: false
@@ -88,8 +132,8 @@ Item {
88 onTriggered: tooltip.visible = tooltip.nextVisible 132 onTriggered: tooltip.visible = tooltip.nextVisible
89 } 133 }
90 134
91 implicitWidth: tooltipText.contentWidth + 16 135 implicitWidth: (tooltipIcon.visible ? tooltipIcon.implicitWidth : 0) + (tooltipIcon.visible && tooltipText.visible ? 8 : 0) + (tooltipText.visible ? tooltipText.implicitWidth : 0) + 16
92 implicitHeight: tooltipText.contentHeight + 16 136 implicitHeight: tooltipText.implicitHeight + 16
93 color: "black" 137 color: "black"
94 138
95 WrapperMouseArea { 139 WrapperMouseArea {
@@ -103,18 +147,63 @@ Item {
103 Item { 147 Item {
104 anchors.fill: parent 148 anchors.fill: parent
105 149
106 Text { 150 Row {
107 id: tooltipText 151 id: tooltipLayout
108 152
109 anchors.centerIn: parent 153 anchors {
154 left: parent.left
155 top: parent.top
156 leftMargin: 8
157 topMargin: 8
158 verticalCenter: parent.verticalCenter
159 }
110 160
111 font.pointSize: 10 161 height: parent.height
112 font.family: "Fira Sans" 162 width: childrenRect.width
113 color: "white"
114 163
115 text: root.state?.tooltip ?? "" 164 spacing: 0
165
166 MaterialDesignIcon {
167 id: tooltipIcon
168
169 implicitSize: 14
170 anchors.verticalCenter: parent.verticalCenter
171
172 visible: tooltip.state.running || !tooltip.state.updating
173
174 icon: tooltip.state.running ? "update" : "timer-off"
175 }
176
177 Item {
178 visible: tooltipIcon.visible && tooltipText.visible
179 height: parent.height
180 width: 8
181 }
182
183 Text {
184 id: tooltipText
185
186 visible: tooltip.state.state?.tooltip ?? false
187
188 anchors.verticalCenter: parent.verticalCenter
189
190 font.pointSize: 10
191 font.family: "Fira Sans"
192 color: "white"
193
194 text: tooltip.state.state?.tooltip ?? ""
195 }
116 } 196 }
117 } 197 }
118 } 198 }
119 } 199 }
200
201 WorktimePopup {
202 state: Worktime.time
203 mouseArea: timeWidget.mouseArea
204 }
205 WorktimePopup {
206 state: Worktime.today
207 mouseArea: todayWidget.mouseArea
208 }
120} 209}
diff --git a/home-modules/quickshell.nix b/home-modules/quickshell.nix
index dac7089f..79c33920 100644
--- a/home-modules/quickshell.nix
+++ b/home-modules/quickshell.nix
@@ -53,6 +53,9 @@ in {
53 Documentation = "https://quickshell.org/docs/v${cfg.package.version}"; 53 Documentation = "https://quickshell.org/docs/v${cfg.package.version}";
54 PartOf = [ "graphical-session.target" ]; 54 PartOf = [ "graphical-session.target" ];
55 After = [ "graphical-session-pre.target" ]; 55 After = [ "graphical-session-pre.target" ];
56 X-Restart-Triggers = [
57 "${config.xdg.configFile."quickshell".source}"
58 ];
56 }; 59 };
57 60
58 Service = { 61 Service = {