diff options
Diffstat (limited to 'accounts')
4 files changed, 301 insertions, 7 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml index dd0feb4b..426ed78c 100644 --- a/accounts/gkleen@sif/shell/quickshell/Bar.qml +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml | |||
| @@ -66,9 +66,9 @@ 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 { command: "time"; } |
| 70 | 70 | ||
| 71 | WorktimeWidget { command: "today"; } | 71 | // WorktimeWidget { command: "today"; } |
| 72 | 72 | ||
| 73 | KeyboardLayout {} | 73 | KeyboardLayout {} |
| 74 | 74 | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml index 589c36e5..a727b044 100644 --- a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml +++ b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml | |||
| @@ -15,7 +15,7 @@ Scope { | |||
| 15 | id: notifsRepeater | 15 | id: notifsRepeater |
| 16 | 16 | ||
| 17 | model: ScriptModel { | 17 | model: ScriptModel { |
| 18 | values: NotificationManager.displayInhibited ? [] : [...NotificationManager.groups] | 18 | values: NotificationManager.displayInhibited ? [] : NotificationManager.groups |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | delegate: PanelWindow { | 21 | delegate: PanelWindow { |
| @@ -48,6 +48,7 @@ Scope { | |||
| 48 | readonly property real spaceAbove: { | 48 | readonly property real spaceAbove: { |
| 49 | var res = 0; | 49 | var res = 0; |
| 50 | for (let i = 0; i < notifWindow.index; i++) { | 50 | for (let i = 0; i < notifWindow.index; i++) { |
| 51 | (_ => {})(notifsRepeater.objectAt(i).modelData); | ||
| 51 | res += notifsRepeater.objectAt(i).height + 8; | 52 | res += notifsRepeater.objectAt(i).height + 8; |
| 52 | } | 53 | } |
| 53 | return res; | 54 | return res; |
| @@ -60,7 +61,7 @@ Scope { | |||
| 60 | 61 | ||
| 61 | color: "transparent" | 62 | color: "transparent" |
| 62 | 63 | ||
| 63 | implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + 16 | 64 | implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + (notifTime.visible ? 8 + notifTime.contentHeight : 0) + 16 |
| 64 | implicitWidth: 400 | 65 | implicitWidth: 400 |
| 65 | 66 | ||
| 66 | WrapperMouseArea { | 67 | WrapperMouseArea { |
| @@ -108,6 +109,8 @@ Scope { | |||
| 108 | res += 1; | 109 | res += 1; |
| 109 | if (notifActions.visible) | 110 | if (notifActions.visible) |
| 110 | res += 1; | 111 | res += 1; |
| 112 | if (notifTime.visible) | ||
| 113 | res += 1; | ||
| 111 | return res; | 114 | return res; |
| 112 | } | 115 | } |
| 113 | 116 | ||
| @@ -167,7 +170,7 @@ Scope { | |||
| 167 | Layout.column: 0 | 170 | Layout.column: 0 |
| 168 | Layout.row: 0 | 171 | Layout.row: 0 |
| 169 | Layout.fillHeight: true | 172 | Layout.fillHeight: true |
| 170 | Layout.rowSpan: notifBody.visible ? 2 : 1 | 173 | Layout.rowSpan: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) |
| 171 | } | 174 | } |
| 172 | 175 | ||
| 173 | Text { | 176 | Text { |
| @@ -176,6 +179,7 @@ Scope { | |||
| 176 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false | 179 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false |
| 177 | text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? "" | 180 | text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? "" |
| 178 | textFormat: Text.RichText | 181 | textFormat: Text.RichText |
| 182 | wrapMode: Text.Wrap | ||
| 179 | 183 | ||
| 180 | font.pointSize: 10 | 184 | font.pointSize: 10 |
| 181 | font.family: "Fira Sans" | 185 | font.family: "Fira Sans" |
| @@ -187,6 +191,32 @@ Scope { | |||
| 187 | Layout.columnSpan: notifCount.visible ? 2 : 1 | 191 | Layout.columnSpan: notifCount.visible ? 2 : 1 |
| 188 | } | 192 | } |
| 189 | 193 | ||
| 194 | Text { | ||
| 195 | id: notifTime | ||
| 196 | |||
| 197 | Connections { | ||
| 198 | target: NotificationManager.clock | ||
| 199 | function onDateChanged() { | ||
| 200 | notifTime.text = NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | visible: notifTime.text && notifTime.text !== "now" | ||
| 205 | text: NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime) | ||
| 206 | |||
| 207 | font.pointSize: 8 | ||
| 208 | font.family: "Fira Sans" | ||
| 209 | font.italic: true | ||
| 210 | color: "#555" | ||
| 211 | maximumLineCount: 1 | ||
| 212 | horizontalAlignment: Text.AlignRight | ||
| 213 | |||
| 214 | Layout.fillWidth: true | ||
| 215 | Layout.row: notifBody.visible ? 2 : 1 | ||
| 216 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 217 | Layout.columnSpan: 2 | ||
| 218 | } | ||
| 219 | |||
| 190 | RowLayout { | 220 | RowLayout { |
| 191 | id: notifActions | 221 | id: notifActions |
| 192 | 222 | ||
| @@ -196,9 +226,9 @@ Scope { | |||
| 196 | uniformCellSizes: true | 226 | uniformCellSizes: true |
| 197 | 227 | ||
| 198 | width: 400 - 16 | 228 | width: 400 - 16 |
| 199 | Layout.row: notifBody.visible ? 2 : 1 | 229 | Layout.row: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) |
| 200 | Layout.column: 0 | 230 | Layout.column: 0 |
| 201 | Layout.columnSpan: notifImage.visible ? 3 : 2 | 231 | Layout.columnSpan: 2 + (notifImage.visible ? 1 : 0) |
| 202 | 232 | ||
| 203 | Repeater { | 233 | Repeater { |
| 204 | model: ScriptModel { | 234 | model: ScriptModel { |
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml index 3dadbc69..1005182a 100644 --- a/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml | |||
| @@ -2,6 +2,9 @@ import Quickshell | |||
| 2 | import QtQuick | 2 | import QtQuick |
| 3 | import Quickshell.Widgets | 3 | import Quickshell.Widgets |
| 4 | import qs.Services | 4 | import qs.Services |
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import QtQuick.Shapes | ||
| 5 | 8 | ||
| 6 | Item { | 9 | Item { |
| 7 | id: root | 10 | id: root |
| @@ -44,4 +47,214 @@ Item { | |||
| 44 | } | 47 | } |
| 45 | } | 48 | } |
| 46 | } | 49 | } |
| 50 | |||
| 51 | Loader { | ||
| 52 | id: tooltipLoader | ||
| 53 | |||
| 54 | active: false | ||
| 55 | |||
| 56 | Connections { | ||
| 57 | target: widgetMouseArea | ||
| 58 | function onContainsMouseChanged() { | ||
| 59 | if (widgetMouseArea.containsMouse) | ||
| 60 | tooltipLoader.active = true; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | sourceComponent: PopupWindow { | ||
| 65 | id: tooltip | ||
| 66 | |||
| 67 | property bool nextVisible: !NotificationManager.displayInhibited && (widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 68 | |||
| 69 | anchor { | ||
| 70 | item: widgetMouseArea | ||
| 71 | edges: Edges.Bottom | Edges.Left | ||
| 72 | } | ||
| 73 | visible: false | ||
| 74 | |||
| 75 | onNextVisibleChanged: hangTimer.restart() | ||
| 76 | |||
| 77 | Timer { | ||
| 78 | id: hangTimer | ||
| 79 | interval: tooltip.visible ? 100 : 500 | ||
| 80 | onTriggered: { | ||
| 81 | tooltip.visible = tooltip.nextVisible; | ||
| 82 | if (!tooltip.visible) | ||
| 83 | tooltipLoader.active = false; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | implicitWidth: 400 | ||
| 88 | implicitHeight: Math.min(tooltip.screen.height * 0.66, Math.max(100, scroll.contentHeight + 16)) | ||
| 89 | color: "black" | ||
| 90 | |||
| 91 | WrapperMouseArea { | ||
| 92 | id: tooltipMouseArea | ||
| 93 | |||
| 94 | hoverEnabled: true | ||
| 95 | enabled: true | ||
| 96 | |||
| 97 | anchors.fill: parent | ||
| 98 | |||
| 99 | WrapperItem { | ||
| 100 | margin: 8 | ||
| 101 | |||
| 102 | ScrollView { | ||
| 103 | id: scroll | ||
| 104 | |||
| 105 | contentWidth: availableWidth | ||
| 106 | // ScrollBar.vertical.policy: ScrollBar.AlwaysOn | ||
| 107 | |||
| 108 | ColumnLayout { | ||
| 109 | id: historyLayout | ||
| 110 | anchors { | ||
| 111 | left: parent.left | ||
| 112 | right: parent.right | ||
| 113 | } | ||
| 114 | |||
| 115 | spacing: 8 | ||
| 116 | |||
| 117 | Repeater { | ||
| 118 | model: ScriptModel { | ||
| 119 | values: [...NotificationManager.history].reverse().map(o => o.notification) | ||
| 120 | } | ||
| 121 | |||
| 122 | delegate: GridLayout { | ||
| 123 | id: notif | ||
| 124 | |||
| 125 | Layout.fillWidth: true | ||
| 126 | Layout.preferredHeight: notifSummary.contentHeight + (notifBody.visible ? notifBody.contentHeight + 8 : 0) + (notifSep.visible ? notifSep.height + 8 : 0) + notifTime.contentHeight + 8 | ||
| 127 | |||
| 128 | required property var modelData | ||
| 129 | required property int index | ||
| 130 | |||
| 131 | columnSpacing: 8 | ||
| 132 | rowSpacing: 8 | ||
| 133 | |||
| 134 | columns: notifImage.visible ? 2 : 1 | ||
| 135 | rows: { | ||
| 136 | var res = 2; | ||
| 137 | if (notifBody.visible) | ||
| 138 | res += 1; | ||
| 139 | if (notifSep.visible) | ||
| 140 | res += 1; | ||
| 141 | return res; | ||
| 142 | } | ||
| 143 | |||
| 144 | Shape { | ||
| 145 | id: notifSep | ||
| 146 | |||
| 147 | visible: notif.index != 0 | ||
| 148 | |||
| 149 | height: 2 | ||
| 150 | width: 400 - 32 | ||
| 151 | |||
| 152 | ShapePath { | ||
| 153 | strokeWidth: 2 | ||
| 154 | strokeColor: "#20ffffff" | ||
| 155 | startX: 0; startY: 0; | ||
| 156 | PathLine { x: 400 - 32; y: 0; } | ||
| 157 | } | ||
| 158 | |||
| 159 | Layout.row: 0 | ||
| 160 | Layout.column: 0 | ||
| 161 | Layout.columnSpan: notifImage.visible ? 2 : 1 | ||
| 162 | Layout.alignment: Qt.AlignHCenter | ||
| 163 | } | ||
| 164 | |||
| 165 | Text { | ||
| 166 | id: notifSummary | ||
| 167 | |||
| 168 | text: notif.modelData?.summary ?? "" | ||
| 169 | |||
| 170 | font.pointSize: 10 | ||
| 171 | font.family: "Fira Sans" | ||
| 172 | font.italic: true | ||
| 173 | color: "white" | ||
| 174 | maximumLineCount: 1 | ||
| 175 | elide: Text.ElideRight | ||
| 176 | |||
| 177 | Layout.fillWidth: true | ||
| 178 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 179 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 180 | } | ||
| 181 | |||
| 182 | Image { | ||
| 183 | id: notifImage | ||
| 184 | |||
| 185 | visible: (notif.modelData?.image || notif.modelData?.appIcon) ?? false | ||
| 186 | |||
| 187 | onStatusChanged: { | ||
| 188 | if (notifImage.status == Image.Error) | ||
| 189 | notifImage.visible = false; | ||
| 190 | } | ||
| 191 | |||
| 192 | source: (notif.modelData?.image || notif.modelData?.appIcon) ?? "" | ||
| 193 | fillMode: Image.PreserveAspectFit | ||
| 194 | asynchronous: true | ||
| 195 | smooth: true | ||
| 196 | mipmap: true | ||
| 197 | |||
| 198 | Layout.maximumWidth: 50 | ||
| 199 | Layout.column: 0 | ||
| 200 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 201 | Layout.fillHeight: true | ||
| 202 | Layout.rowSpan: notifBody.visible ? 3 : 2 | ||
| 203 | } | ||
| 204 | |||
| 205 | Text { | ||
| 206 | id: notifBody | ||
| 207 | |||
| 208 | visible: notif.modelData?.body ?? false | ||
| 209 | text: notif.modelData?.body ?? "" | ||
| 210 | textFormat: Text.RichText | ||
| 211 | wrapMode: Text.Wrap | ||
| 212 | |||
| 213 | font.pointSize: 10 | ||
| 214 | font.family: "Fira Sans" | ||
| 215 | color: "white" | ||
| 216 | |||
| 217 | Layout.fillWidth: true | ||
| 218 | Layout.row: notifSep.visible ? 2 : 1 | ||
| 219 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 220 | } | ||
| 221 | |||
| 222 | Text { | ||
| 223 | id: notifTime | ||
| 224 | |||
| 225 | Connections { | ||
| 226 | target: NotificationManager.clock | ||
| 227 | function onDateChanged() { | ||
| 228 | notifTime.text = NotificationManager.formatTime(notif.modelData?.receivedTime); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | text: NotificationManager.formatTime(notif.modelData?.receivedTime) | ||
| 233 | |||
| 234 | font.pointSize: 8 | ||
| 235 | font.family: "Fira Sans" | ||
| 236 | font.italic: true | ||
| 237 | color: "#555" | ||
| 238 | maximumLineCount: 1 | ||
| 239 | horizontalAlignment: Text.AlignRight | ||
| 240 | |||
| 241 | Layout.fillWidth: true | ||
| 242 | Layout.row: { | ||
| 243 | var res = 1; | ||
| 244 | if (notifSep.visible) | ||
| 245 | res += 1; | ||
| 246 | if (notifBody.visible) | ||
| 247 | res += 1; | ||
| 248 | return res; | ||
| 249 | } | ||
| 250 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 47 | } | 260 | } |
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml index 778cdc2a..2199ccdf 100644 --- a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml +++ b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml | |||
| @@ -56,6 +56,8 @@ Singleton { | |||
| 56 | { "test": { "appName": "Element" }, "group-by": [ "summary" ] } | 56 | { "test": { "appName": "Element" }, "group-by": [ "summary" ] } |
| 57 | ]; | 57 | ]; |
| 58 | 58 | ||
| 59 | property var history: [] | ||
| 60 | |||
| 59 | Component { | 61 | Component { |
| 60 | id: expirationTimer | 62 | id: expirationTimer |
| 61 | 63 | ||
| @@ -74,6 +76,45 @@ Singleton { | |||
| 74 | } | 76 | } |
| 75 | } | 77 | } |
| 76 | 78 | ||
| 79 | Component { | ||
| 80 | id: notificationLock | ||
| 81 | |||
| 82 | RetainableLock {} | ||
| 83 | } | ||
| 84 | |||
| 85 | readonly property SystemClock clock: SystemClock { | ||
| 86 | precision: SystemClock.Minutes | ||
| 87 | } | ||
| 88 | |||
| 89 | function formatTime(time) { | ||
| 90 | const now = root.clock.date; | ||
| 91 | const diff = now - time; | ||
| 92 | const minutes = Math.ceil(diff / 60000); | ||
| 93 | const hours = Math.floor(minutes / 60); | ||
| 94 | |||
| 95 | if (hours < 1) { | ||
| 96 | if (minutes < 1) | ||
| 97 | return "now"; | ||
| 98 | if (minutes == 1) | ||
| 99 | return "1 minute"; | ||
| 100 | return `${minutes} minutes`; | ||
| 101 | } | ||
| 102 | |||
| 103 | const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()) | ||
| 104 | const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()) | ||
| 105 | const days = Math.floor((nowDate - timeDate) / (1000 * 86400)) | ||
| 106 | |||
| 107 | const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm"); | ||
| 108 | |||
| 109 | if (days === 0) | ||
| 110 | return timeStr; | ||
| 111 | if (days === 1) | ||
| 112 | return `yesterday ${timeStr}`; | ||
| 113 | |||
| 114 | const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD"); | ||
| 115 | return `${dateStr} ${timeStr}`; | ||
| 116 | } | ||
| 117 | |||
| 77 | NotificationServer { | 118 | NotificationServer { |
| 78 | id: server | 119 | id: server |
| 79 | 120 | ||
| @@ -92,7 +133,17 @@ Singleton { | |||
| 92 | Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true }); | 133 | Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true }); |
| 93 | notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout }); | 134 | notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout }); |
| 94 | } | 135 | } |
| 136 | Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true }); | ||
| 137 | notification.receivedTime = root.clock.date; | ||
| 138 | notification.closed.connect((reason) => server.onNotificationClosed(notification, reason)); | ||
| 95 | notification.tracked = true; | 139 | notification.tracked = true; |
| 96 | } | 140 | } |
| 141 | |||
| 142 | function onNotificationClosed(notification, reason) { | ||
| 143 | root.history.push({ | ||
| 144 | lock: notificationLock.createObject(root, { locked: true, object: notification }), | ||
| 145 | notification: notification | ||
| 146 | }); | ||
| 147 | } | ||
| 97 | } | 148 | } |
| 98 | } | 149 | } |
