diff options
Diffstat (limited to 'accounts/gkleen@sif/shell')
| -rw-r--r-- | accounts/gkleen@sif/shell/quickshell/Bar.qml | 4 | ||||
| -rw-r--r-- | accounts/gkleen@sif/shell/quickshell/Services/Worktime.qml | 75 | ||||
| -rw-r--r-- | accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml | 193 |
3 files changed, 217 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 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQuick | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Io | ||
| 6 | import QtQml | ||
| 7 | |||
| 8 | Singleton { | ||
| 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 @@ | |||
| 1 | import QtQml | 1 | import QtQml |
| 2 | import Quickshell | 2 | import Quickshell |
| 3 | import Quickshell.Io | ||
| 4 | import QtQuick | 3 | import QtQuick |
| 5 | import Quickshell.Widgets | 4 | import Quickshell.Widgets |
| 5 | import qs.Services | ||
| 6 | 6 | ||
| 7 | Item { | 7 | Item { |
| 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 | } |
