diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-15 14:37:21 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-15 14:37:21 +0200 |
| commit | bf4b1ec622122a713f975731839f430756823202 (patch) | |
| tree | 5c822a64faaa2a76b4dafae4397a1c3070ed57ff /desktop | |
| parent | fe1c0524c87118b1171c6549c68d7fbb701c61ab (diff) | |
| download | quickshell-bf4b1ec622122a713f975731839f430756823202.tar.gz quickshell-bf4b1ec622122a713f975731839f430756823202.zip | |
feat(desktop): fill the drawer's notification space
The reserved Item becomes the live queue with a History button, scrollable
because the queue can hold 20. The drawer writes notifyd/drawer so the
balloon shell stands down while this space is showing, which is what stops a
notification appearing twice.
Diffstat (limited to 'desktop')
| -rw-r--r-- | desktop/Drawer.qml | 26 | ||||
| -rw-r--r-- | desktop/NotificationList.qml | 90 | ||||
| -rw-r--r-- | desktop/NotificationRow.qml | 89 |
3 files changed, 199 insertions, 6 deletions
diff --git a/desktop/Drawer.qml b/desktop/Drawer.qml index 4ed37d3..79a5393 100644 --- a/desktop/Drawer.qml +++ b/desktop/Drawer.qml @@ -10,6 +10,7 @@ // GNU General Public License for more details. import Quickshell +import Quickshell.Io import Quickshell.Wayland import QtQuick @@ -29,6 +30,20 @@ Scope { // Which module's page is showing. Empty means the grid. property string page: "" + // Set by NotificationList's History button. Task 5 reads this to show the + // history view; nothing consumes it yet. + property bool history: false + + // The balloons read this to stand down while the drawer holds the space. + FileView { + id: drawerFlag + path: (Quickshell.env("XDG_RUNTIME_DIR") || "/tmp") + "/notifyd/drawer" + atomicWrites: true + printErrors: false + } + + onOpenChanged: drawerFlag.setText(root.open ? "1\n" : "0\n") + readonly property var screenObj: Quickshell.screens.find(s => s.name === root.monitor) ?? Quickshell.screens[0] @@ -123,16 +138,15 @@ Scope { anchors.margins: 16 visible: root.page === "" - // Reserved for the notification engine. An empty Item that - // claims the space rather than a placeholder graphic: the - // grid has to sit where it will sit once notifications - // arrive, or the layout is tuned against a position that - // does not survive. - Item { + // The notification engine's space. The live queue lists + // here while the drawer is open; the balloons stand down + // because the drawer flag above is set. + NotificationList { id: notifications anchors { top: parent.top; left: parent.left; right: parent.right } anchors.bottom: grid.top anchors.bottomMargin: 16 + onHistory: root.history = true } // Fixed, never scrolled. Three columns at 600px with a diff --git a/desktop/NotificationList.qml b/desktop/NotificationList.qml new file mode 100644 index 0000000..9c8ae26 --- /dev/null +++ b/desktop/NotificationList.qml @@ -0,0 +1,90 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import QtQuick + +// The drawer's reserved notification space: a header with a History button, +// then the live queue, scrollable because the queue can hold 20 and the grid +// below is fixed. The grid owns its own position, so this space only fills +// what the grid leaves. +Item { + id: list + + signal history + + Column { + anchors.fill: parent + spacing: 6 + + Item { + width: parent.width + height: 28 + + Text { + anchors { left: parent.left; verticalCenter: parent.verticalCenter } + text: "Notifications" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4; bold: true } + color: Theme.subtext + } + + Text { + id: historyBtn + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + text: "History" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: historyArea.containsMouse ? Theme.accent : Theme.subtext + + MouseArea { + id: historyArea + anchors.fill: parent + anchors.margins: -6 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: list.history() + } + } + } + + Flickable { + id: flick + width: parent.width + height: parent.height - 34 + clip: true + contentWidth: width + contentHeight: col.implicitHeight + boundsBehavior: Flickable.StopAtBounds + + Column { + id: col + width: flick.width + spacing: 2 + + Repeater { + model: Notify.queue + NotificationRow { + required property var modelData + width: col.width + notification: modelData + live: true + } + } + + Text { + width: col.width + visible: Notify.queue.length === 0 + text: "No notifications" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.overlay + } + } + } + } +} diff --git a/desktop/NotificationRow.qml b/desktop/NotificationRow.qml new file mode 100644 index 0000000..f95f3b4 --- /dev/null +++ b/desktop/NotificationRow.qml @@ -0,0 +1,89 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import QtQuick + +// One notification in the drawer: app, summary and body. Live rows close with +// the X and click through to their actions; a history row is inert. +Item { + id: row + + required property var notification + // A history row has no live client: no X, and a click does nothing. + property bool live: true + + readonly property bool inert: row.notification.expires !== 0 && Date.now() >= row.notification.expires + + implicitHeight: texts.implicitHeight + 16 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (!row.live) return; + const acts = row.notification.actions || []; + if (acts.length > 0 && !row.inert) Notify.actions(row.notification.id, acts); + else Notify.close(row.notification.id); + } + } + + Column { + id: texts + anchors { + left: parent.left + right: closeBtn.visible ? closeBtn.left : parent.right + rightMargin: 10 + verticalCenter: parent.verticalCenter + } + spacing: 2 + + Text { + width: parent.width + text: (row.notification.app || "") + (row.notification.summary ? " " + row.notification.summary : "") + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + color: Theme.text + } + + Text { + width: parent.width + visible: text !== "" + text: row.notification.body || "" + textFormat: Text.RichText + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } + color: Theme.subtext + } + } + + Text { + id: closeBtn + visible: row.live + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + width: 20 + height: 20 + text: "\uf00d" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font { family: Theme.iconFamily; pixelSize: 11 } + color: closeArea.containsMouse ? Theme.red : Theme.subtext + + MouseArea { + id: closeArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: Notify.close(row.notification.id) + } + } +} |
