aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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>