summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);