aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-24 18:56:08 +0200
committerDanilo M. <danix@danix.xyz>2026-09-24 18:56:08 +0200
commit35b136944656c9ff9c3b064f47be08224f76c1f9 (patch)
tree1668716a498c5d292dec57ea0710f454193c5ce9
parentab785283e6abd1730efd4499ba4bc436a3de2665 (diff)
downloadqtmaildir-35b136944656c9ff9c3b064f47be08224f76c1f9.tar.gz
qtmaildir-35b136944656c9ff9c3b064f47be08224f76c1f9.zip
feat: close the calendar's event pane with Escape
Escape was scoped to the pane and only cancelled an edit, so once an event was selected nothing closed the pane again. It is a window shortcut now, labelled Close event: with no edit open it clears the selection and the pane hides. With an edit open it cancels only when focus is inside the pane, so a stray Escape after clicking the grid cannot discard the form. That guard needed the month grid to take focus on a click, which it never did; the view-scoped PgUp/PgDn, Delete and Ctrl+Z were likewise reachable only by Tab until now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--AGENTS.md4
-rw-r--r--docs/superpowers/specs/2026-09-24-calendar-design.md4
-rw-r--r--src/calendarwindow.cpp22
-rw-r--r--src/monthview.cpp3
-rw-r--r--tests/test_calendarwindow.cpp19
-rw-r--r--translations/qtmaildir_it_IT.ts4
6 files changed, 47 insertions, 9 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 6b69818..0697b20 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1059,7 +1059,9 @@ scoped to the views with `Qt::WidgetWithChildrenShortcut`.** They are window
shortcuts otherwise, and a bare `Delete` or `Ctrl+Z` then fires while the user
is typing in the edit form's Title field, deleting the event or undoing the
calendar instead of editing text. `EventPane` and the stacked views are the
-scopes; Escape is scoped to the pane.
+scopes. Escape is the exception and is a WINDOW shortcut, so it closes the
+details pane from the grid; with an edit open it cancels only when focus is
+inside the pane, so a stray Escape from the grid cannot discard the form.
**`calendar_sync_command` is read through a `QStringList` join.** With no quote
around the value, QSettings splits a comma-holding string into a `QStringList`
diff --git a/docs/superpowers/specs/2026-09-24-calendar-design.md b/docs/superpowers/specs/2026-09-24-calendar-design.md
index a57bb26..f58f372 100644
--- a/docs/superpowers/specs/2026-09-24-calendar-design.md
+++ b/docs/superpowers/specs/2026-09-24-calendar-design.md
@@ -312,7 +312,9 @@ Edit and View holding every action; `Ctrl+N` New, `Ctrl+E` Edit, `Delete`,
`Home` would otherwise fire while the cursor is in the edit form's Title field,
deleting the event or moving the month instead of editing text. `Delete`,
`PgUp`/`PgDn` and `Home` sit on the stacked views; Undo and Redo sit there too;
-Escape is scoped to `EventPane`. `Ctrl+N`, `Ctrl+E` and `Ctrl+W` stay window
+Escape is a window shortcut: it closes the details pane (the pane is hidden
+while nothing is selected) and cancels an edit only when focus is inside
+`EventPane`. `Ctrl+N`, `Ctrl+E` and `Ctrl+W` stay window
shortcuts, since no text field wants them.
**i18n.** Every string in `tr()`; the Italian `.ts` refreshed, `lrelease`
diff --git a/src/calendarwindow.cpp b/src/calendarwindow.cpp
index d9c1da2..4a07b89 100644
--- a/src/calendarwindow.cpp
+++ b/src/calendarwindow.cpp
@@ -25,6 +25,7 @@
#include "monthview.h"
#include <QAction>
+#include <QApplication>
#include <QButtonGroup>
#include <QCloseEvent>
#include <QComboBox>
@@ -276,8 +277,10 @@ void CalendarWindow::buildActions()
QKeySequence(Qt::CTRL | Qt::Key_E), nullptr, &CalendarWindow::startEdit);
QAction *del = make(QStringLiteral("deleteEvent"), tr("&Delete event"),
QKeySequence(Qt::Key_Delete), m_views, &CalendarWindow::deleteSelected);
- QAction *cancel = make(QStringLiteral("cancelEdit"), tr("Cancel &editing"),
- QKeySequence(Qt::Key_Escape), m_pane, &CalendarWindow::cancelEdit);
+ // Window-wide, so Escape closes the details pane from the grid too; the
+ // combo and spin box popups take their own Escape first.
+ QAction *cancel = make(QStringLiteral("cancelEdit"), tr("C&lose event"),
+ QKeySequence(Qt::Key_Escape), nullptr, &CalendarWindow::cancelEdit);
QAction *undo = m_undo.createUndoAction(this, tr("&Undo"));
undo->setShortcut(QKeySequence::Undo);
@@ -619,9 +622,18 @@ bool CalendarWindow::save()
void CalendarWindow::cancelEdit()
{
- if (!m_pane->isEditing())
- return;
- m_pane->stopEdit();
+ // Cancelling an edit returns to the details; with no edit, closing the
+ // details clears the selection, which hides the pane.
+ if (m_pane->isEditing()) {
+ // Escape used to be scoped to the pane; keep a stray one from the
+ // grid from throwing away an edit.
+ if (!m_pane->isAncestorOf(QApplication::focusWidget()))
+ return;
+ m_pane->stopEdit();
+ } else {
+ m_selectedUid.clear();
+ m_selectedStart = {};
+ }
rebuildItems();
}
diff --git a/src/monthview.cpp b/src/monthview.cpp
index 202ef4a..5b56ee4 100644
--- a/src/monthview.cpp
+++ b/src/monthview.cpp
@@ -28,6 +28,9 @@ MonthView::MonthView(QWidget *parent)
m_year(QDate::currentDate().year()), m_month(QDate::currentDate().month())
{
setMinimumSize(420, 320);
+ // A click must move focus here, or it stays in the edit form and the
+ // view-scoped shortcuts and the Escape guard read the wrong widget.
+ setFocusPolicy(Qt::StrongFocus);
}
void MonthView::setMonth(int year, int month)
diff --git a/tests/test_calendarwindow.cpp b/tests/test_calendarwindow.cpp
index 5591b2b..ea83f12 100644
--- a/tests/test_calendarwindow.cpp
+++ b/tests/test_calendarwindow.cpp
@@ -128,6 +128,7 @@ private slots:
void aFreshWindowShowsTheCurrentMonth();
void aStaleSaveCanBeRetried();
void aRolledBackWriteIsNotReportedLostAfterSync();
+ void escapeClosesTheDetailsButNotAnEditFromOutsideThePane();
};
void TestCalendarWindow::loadsAndSelects()
@@ -320,5 +321,23 @@ void TestCalendarWindow::aRolledBackWriteIsNotReportedLostAfterSync()
5000);
}
+
+void TestCalendarWindow::escapeClosesTheDetailsButNotAnEditFromOutsideThePane()
+{
+ Fixture f;
+ std::unique_ptr<CalendarWindow> w(f.window());
+ auto *close = w->findChild<QAction *>(QStringLiteral("cancelEdit"));
+ QVERIFY(close);
+ QVERIFY(w->selectEvent(QStringLiteral("one@example.org")));
+ close->trigger();
+ QVERIFY(w->selectedUid().isEmpty());
+
+ // Focus is nowhere inside the pane, as after a click on the grid.
+ QVERIFY(w->selectEvent(QStringLiteral("one@example.org")));
+ editTitle(w.get(), QStringLiteral("Changed"));
+ close->trigger();
+ QVERIFY(w->isEditing());
+}
+
QTEST_MAIN(TestCalendarWindow)
#include "test_calendarwindow.moc"
diff --git a/translations/qtmaildir_it_IT.ts b/translations/qtmaildir_it_IT.ts
index 35702ae..eff6754 100644
--- a/translations/qtmaildir_it_IT.ts
+++ b/translations/qtmaildir_it_IT.ts
@@ -71,8 +71,8 @@
<translation>&amp;Elimina evento</translation>
</message>
<message>
- <source>Cancel &amp;editing</source>
- <translation>Annulla &amp;modifica</translation>
+ <source>C&amp;lose event</source>
+ <translation>C&amp;hiudi evento</translation>
</message>
<message>
<source>&amp;Undo</source>