summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-13 16:35:37 +0200
committerDanilo M. <danix@danix.xyz>2026-08-13 16:35:37 +0200
commit1dd39414fd6139f2934554fc90b1c5721603d14b (patch)
treedf825342f0b2e3d49ff6c6f9ba5c65598a6fad14
parent0422205803c8f6980a447fdaa1dc270a486970cd (diff)
downloadqtmaildir-1dd39414fd6139f2934554fc90b1c5721603d14b.tar.gz
qtmaildir-1dd39414fd6139f2934554fc90b1c5721603d14b.zip
docs: the window size cannot be restored under a tiling compositor
Item 75 shipped claiming the rules window remembers its size. It does not, and no code here can make it. Hyprland tiles the window to fill its slot, so the size dragged belongs to the tile. saveGeometry stores frameGeometry beside normalGeometry and restoreGeometry restores the normal one, which stays at whatever resize() last set it to. Decoded from the real state file after a hand test: frame 2248x806, normal 760x664. The dialog restores 760 correctly and still opens tiled. Three diagnoses were tried before this one and each was disproved by a probe rather than argued away: that restoreGeometry rejected the blob as off-screen, that the layout overrode a geometry applied before the first show, and that a test could tell the broken and fixed versions apart. The last one matters most: the offscreen platform returns an identical frame for both, so a size assertion passed against the bug and a mutation restoring it left the suite green. That assertion is not reinstated. The column widths, which are what actually works, keep their test. The changelog and the backlog entry are corrected to say what ships, and CLAUDE.md gains both the tiling-compositor trap and the rule that the offscreen platform cannot test window sizing at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
-rw-r--r--CHANGELOG.md8
-rw-r--r--CLAUDE.md19
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md27
-rw-r--r--tests/test_tagrules.cpp8
4 files changed, 55 insertions, 7 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0637cd9..bbcc854 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,9 +26,11 @@ point at which they are stable.
on disk, so a folder that exists but has no mail in it yet is still offered.
It stays editable, so a folder in the rules file that is no longer on disk
still opens and still saves.
-- The tagging rules window remembers its size and the widths of the rule
- list's columns. A column you widen also survives adding or deleting a rule,
- which previously reset it.
+- The tagging rules window remembers the widths of the rule list's columns. A
+ column you widen also survives adding or deleting a rule, which previously
+ reset it. The window's own size is saved too, but a tiling window manager
+ sizes the window itself, so there it opens at whatever size the tile gives
+ it.
### Fixed
diff --git a/CLAUDE.md b/CLAUDE.md
index 107fe28..6ab47b3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -328,6 +328,25 @@ one route out of three. Assert every route. Underneath sits a second trap:
`done()`, so a test for the closed path has to `show()` the dialog first or it
asserts nothing at all.
+**Under a tiling compositor a window's size is not the application's to
+restore, and the user's desktop is Hyprland.** `saveGeometry` stores
+`frameGeometry` and `normalGeometry`; `restoreGeometry` restores the NORMAL
+one. When the compositor tiles the window to fill its slot, the size the user
+drags is the tile's, and `normalGeometry` keeps whatever the code last passed to
+`resize()`. Measured against the real state file after a hand test: frame
+2248x806, normal 760x664, so the dialog correctly restored 760 and correctly
+looked broken. A whole session went into "the geometry restore is broken" before
+the blob was decoded. Decode the stored geometry before theorising, and expect
+`maximized` to read as a value no bool should hold, which is the tiled state Qt
+records and does not round-trip.
+
+The corollary for tests: **the offscreen platform cannot test window sizing at
+all.** It prints "This plugin does not support propagateSizeHints()" and returns
+an identical frame for a correct restore and a broken one, verified in a
+standalone program containing none of this project's code. A size assertion
+there passes against both, and a mutation putting the bug back leaves the suite
+green. Assert on the stored value, and leave the frame to a hand test.
+
**A queued load can outlive the state that started it.** `loadThread` crosses to the worker
on a queued connection, so its reply lands after whatever the UI did in the meantime. The
generation counter covers a superseded *query*, not a superseded *selection*: blanking the
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
index bce5742..e9f18d9 100644
--- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
+++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
@@ -4764,10 +4764,29 @@ the test asserts on the saved value rather than on the resulting frame.
**Size: S.**
-**Done 2026-08-13** on `rule-builder`, unreleased. `saveGeometry()` and the
-list header's `saveState()` go to `tagrules/geometry` and `tagrules/header` in
-`uistate.conf`, written on `closeEvent` so a size survives Cancel as well as
-Save; `resize(760, 520)` stays as the first-run fallback.
+**Done 2026-08-13** on `rule-builder`, unreleased, for the COLUMN WIDTHS.
+`saveGeometry()` and the list header's `saveState()` go to `tagrules/geometry`
+and `tagrules/header` in `uistate.conf`, written on `done(int)` so they survive
+Cancel as well as Save; `resize(760, 520)` stays as the first-run fallback.
+
+**The window SIZE does not come back, and that half of the item cannot be
+fixed here.** The user's desktop is Hyprland, a tiling compositor. It tiles the
+window to fill its slot, so the size dragged is the tile's; `saveGeometry`
+records `frameGeometry` and `normalGeometry` and `restoreGeometry` restores the
+NORMAL one, which stays at whatever `resize()` last set. Decoded from the real
+state file after a hand test: frame 2248x806, normal 760x664. The code restores
+760 faithfully and the window still opens tiled.
+
+Three wrong diagnoses were tried and each was disproved by a probe rather than
+by argument: that `restoreGeometry` rejected the blob as off-screen (it returns
+true on the real display; the negative y is the DP-1 origin), that the layout
+overrode a geometry set before the first show (a `showEvent` restore produced
+the identical size), and that the offscreen test could tell the two apart (it
+returns the same frame for both, so the mutation survived).
+
+Nothing worth building remains unless the user wants the dialog to open at a
+remembered size when floated, which needs a Hyprland window rule rather than
+code here.
**The approach above was wrong on one point, and a test caught it.** It said to
drop the `resizeColumnToContents` calls "once a saved header state exists",
diff --git a/tests/test_tagrules.cpp b/tests/test_tagrules.cpp
index 0d81df7..8ca7671 100644
--- a/tests/test_tagrules.cpp
+++ b/tests/test_tagrules.cpp
@@ -696,6 +696,14 @@ void TestTagRules::theWindowSizeAndColumnWidthsSurviveAReopen()
// Asserted on the stored VALUE, not on the reopened frame. Item 46: the
// offscreen platform does not honour a resize, so a frame comparison here
// would report a failure the code did not cause.
+ //
+ // And on a TILING compositor the frame is not the dialog's to restore at
+ // all. saveGeometry stores frameGeometry beside normalGeometry, and
+ // restoreGeometry restores the NORMAL one; under Hyprland the window is
+ // tiled to fill its slot, so the size the user drags belongs to the tile
+ // while normalGeometry stays at whatever the code last resize()d it to.
+ // Measured against the real state file: frame 2248x806, normal 760x664.
+ // Restoring 760 there is correct behaviour, not the bug it looks like.
QSettings state(MainWindow::uiStatePath(), QSettings::IniFormat);
QCOMPARE(state.value(QStringLiteral("tagrules/geometry")).toByteArray()
.isEmpty(), false);