diff options
| -rw-r--r-- | mail-overview/Button.qml | 45 | ||||
| -rw-r--r-- | mail-overview/MailPanel.qml | 213 | ||||
| -rw-r--r-- | mail-overview/shell.qml | 14 |
3 files changed, 257 insertions, 15 deletions
diff --git a/mail-overview/Button.qml b/mail-overview/Button.qml new file mode 100644 index 0000000..d6ca78f --- /dev/null +++ b/mail-overview/Button.qml @@ -0,0 +1,45 @@ +// 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 + +Rectangle { + id: btn + property alias text: label.text + property bool danger: false + property bool enabled: true + signal clicked + + implicitWidth: label.implicitWidth + 22 + implicitHeight: label.implicitHeight + 12 + radius: 7 + opacity: enabled ? 1 : 0.4 + color: area.containsMouse && enabled + ? Qt.alpha(danger ? Theme.red : Theme.accent, 0.25) + : Qt.alpha(Theme.surface, 0.8) + border.width: 1 + border.color: Qt.alpha(danger ? Theme.red : Theme.text, 0.2) + + Text { + id: label + anchors.centerIn: parent + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: danger ? Theme.red : Theme.text + } + + MouseArea { + id: area + anchors.fill: parent + hoverEnabled: true + cursorShape: btn.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: if (btn.enabled) btn.clicked() + } +} diff --git a/mail-overview/MailPanel.qml b/mail-overview/MailPanel.qml index 7e81a33..7457fb2 100644 --- a/mail-overview/MailPanel.qml +++ b/mail-overview/MailPanel.qml @@ -23,7 +23,12 @@ Scope { readonly property var screenObj: Quickshell.screens.find(s => s.name === root.monitor) ?? Quickshell.screens[0] - function show() { root.open = true; } + function show() { + // Mail has almost certainly arrived since this was last opened, and + // for an autostarted shell that is the whole session. + Accounts.refresh(); + root.open = true; + } function close() { root.open = false; } function toggle() { root.open ? close() : show(); } @@ -38,4 +43,210 @@ Scope { mask: Region {} WlrLayershell.keyboardFocus: WlrKeyboardFocus.None } + + LazyLoader { + active: root.open + + PanelWindow { + id: win + screen: root.screenObj + + anchors { top: true; left: true; right: true; bottom: true } + color: "transparent" + + // Normal, not Ignore: waybar claims an exclusive zone at the top + // of this screen, so respecting it puts the drawer below the bar + // without this file knowing the bar's height. The backdrop then + // starts below waybar too, which is why waybar stays un-dimmed. + exclusionMode: ExclusionMode.Normal + + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.namespace: "quickshell-mail" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + + Rectangle { + anchors.fill: parent + color: "#000000" + opacity: 0.5 + MouseArea { anchors.fill: parent; onClicked: root.close() } + } + + // Keys reach a focused item, never the window itself: setting + // keyboardFocus above is necessary but not sufficient, and + // Keys.onEscapePressed on a PanelWindow never fires. + Item { + anchors.fill: parent + focus: true + Keys.onEscapePressed: root.close() + } + + // Top right, under the waybar icon, which sits in modules-right. + Rectangle { + id: drawer + anchors { top: parent.top; right: parent.right; topMargin: 8; rightMargin: 8 } + width: 460 + height: Math.min(content.implicitHeight + 32, win.height - 24) + radius: 14 + color: Qt.alpha(Theme.base, 0.72) + border.width: 1 + border.color: Qt.alpha(Theme.text, 0.12) + + // Swallows clicks so they do not reach the backdrop and close + // the drawer. + MouseArea { anchors.fill: parent } + + Column { + id: content + anchors { left: parent.left; right: parent.right; top: parent.top; margins: 16 } + spacing: 10 + + Item { + width: parent.width + implicitHeight: Math.max(title.implicitHeight, totalText.implicitHeight) + + Text { + id: title + anchors.verticalCenter: parent.verticalCenter + text: "Mail" + color: Theme.text + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize + 2 + font.bold: true + } + + Text { + id: totalText + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + // A dash rather than a possibly-wrong number while + // any account is still unknown. + text: Accounts.anyUnknown ? "—" : `${Accounts.total} unread` + color: Theme.subtext + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize + } + } + + Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.12) } + + Text { + visible: Accounts.error !== "" + width: parent.width + text: Accounts.error + color: Theme.red + wrapMode: Text.WordWrap + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize - 2 + } + + Repeater { + model: Accounts.accounts + + Column { + required property var modelData + width: content.width + spacing: 4 + + Item { + width: parent.width + implicitHeight: 26 + + Rectangle { + id: dot + anchors.verticalCenter: parent.verticalCenter + width: 8; height: 8; radius: 4 + // The account's own colour from the config. + // Per-account identity, not a palette. + color: modelData.color || Theme.accent + } + + Text { + anchors { left: dot.right; leftMargin: 10; verticalCenter: parent.verticalCenter } + text: modelData.label + color: Theme.text + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize + } + + Text { + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + text: modelData.count < 0 ? "—" : String(modelData.count) + color: modelData.count > 0 ? Theme.text : Theme.subtext + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize + font.bold: modelData.count > 0 + } + } + + // The newest three unread threads. Read-only: + // qtmaildir takes no arguments, so there is no way + // to ask it for a particular thread. + Repeater { + model: modelData.threads + + Column { + required property var modelData + width: content.width - 18 + x: 18 + spacing: 1 + bottomPadding: 4 + + Item { + width: parent.width + implicitHeight: who.implicitHeight + + Text { + id: who + anchors.left: parent.left + width: parent.width - when.implicitWidth - 10 + text: modelData.authors + elide: Text.ElideRight + color: Theme.subtext + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize - 3 + } + + Text { + id: when + anchors.right: parent.right + text: modelData.date + color: Theme.overlay + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize - 3 + } + } + + Text { + width: parent.width + text: modelData.subject + elide: Text.ElideRight + color: Theme.text + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSize - 2 + } + } + } + } + } + + Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.12) } + + Row { + anchors.right: parent.right + spacing: 10 + + Button { + text: Accounts.syncing ? "Syncing..." : "Sync now" + enabled: !Accounts.syncing + onClicked: Accounts.sync() + } + + Button { + text: "Open qtmaildir" + onClicked: { Accounts.openClient(); root.close(); } + } + } + } + } + } + } } diff --git a/mail-overview/shell.qml b/mail-overview/shell.qml index 7842bc6..cd2e907 100644 --- a/mail-overview/shell.qml +++ b/mail-overview/shell.qml @@ -11,7 +11,6 @@ import Quickshell import Quickshell.Io -import QtQuick ShellRoot { MailPanel { id: panel } @@ -22,17 +21,4 @@ ShellRoot { function show() { panel.show(); } function close() { panel.close(); } } - - // TEMPORARY probe for Task 3 verification. Removed in Step 5. - Component.onCompleted: Qt.callLater(() => { - console.log("ACCOUNTS", JSON.stringify(Accounts.accounts.map(a => a.key + ":" + a.label))); - }) - Connections { - target: Accounts - function onLoadingChanged() { - if (!Accounts.loading) - console.log("COUNTS", JSON.stringify(Accounts.accounts.map(a => a.label + "=" + a.count)), - "total", Accounts.total); - } - } } |
