diff options
| -rw-r--r-- | CHANGELOG.md | 8 | ||||
| -rw-r--r-- | CLAUDE.md | 19 | ||||
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 27 | ||||
| -rw-r--r-- | tests/test_tagrules.cpp | 8 |
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 @@ -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); |
