aboutsummaryrefslogtreecommitdiffstats
path: root/mail-overview
diff options
context:
space:
mode:
Diffstat (limited to 'mail-overview')
-rw-r--r--mail-overview/Button.qml45
-rw-r--r--mail-overview/MailPanel.qml213
-rw-r--r--mail-overview/shell.qml14
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);
- }
- }
}