diff options
Diffstat (limited to 'docs/superpowers')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 123 |
1 files changed, 98 insertions, 25 deletions
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 21e7a91..8b3858b 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 @@ -53,7 +53,7 @@ taking that too literally. | 2 | No way to see full message details (From/To/Cc/Subject) | information | M | **done** | | 3 | Too few clickable affordances, shortcuts are the only route | discoverability | M | **done** | | 4 | Message-pane font size does not survive restart | persistence | S | **done** | -| 5 | Thread list is cramped, poor readability | presentation | S | open | +| 5 | Thread list is cramped, poor readability | presentation | S | **done** | | 6 | Opened message stays unread | behavior | S | **done** | | 7 | HTML view should be default for HTML messages | behavior | XS | **done** (already worked) | | 8 | No buttons or menu entries for archive, undo, etc | discoverability | M | **done** | @@ -446,20 +446,36 @@ to copy. Two concrete sub-items, from using the list rather than looking at it: - **"All items look unread (bold), maybe use regular for read items?"** - **Done 2026-08-07, and the guess written here was wrong on both branches.** - - Bold was indeed already conditional, and the user's list was not mostly - unread, and bold was not leaking. The actual cause: **bold renders - identically to regular on the user's system.** Confirmed by eye against a - bare `QTableView` holding a plain `QStandardItemModel` with no qtmaildir code - involved, so the fault is in Qt or fontconfig, below this application, and - nothing in the model could ever have reached it. Bold was unread's ONLY cue. - - The fix inverts the emphasis instead: unread rows keep the palette's text - colour and READ rows are dimmed toward the background, via - `ThreadListModel::readColour()`. The cue rides on `Qt::ForegroundRole`, costs - no column, and suits the real ratio, which was 99 unread against 4220 read. - Bold is kept, since it works on other systems, but nothing depends on it. + **Resolved 2026-08-07. The cause was a misconfigured desktop font, not code.** + + The user's Qt font was set to **Bold in qt6ct**, so every row rendered bold + and nothing could stand out. Bold in the model was working correctly the + whole time. Correcting the qt6ct setting fixed the original complaint on its + own. + + **Read this before trusting any measurement in this file.** Three wrong + conclusions were reached before that came out, and the reasoning behind each + is worth keeping, because the same mistakes are easy to repeat. + + 1. Dismissed from thread counts (99 unread against 4220 read), which + explained why two screenshots looked alike but said nothing about whether + bold rendered. + 2. Dismissed again by a probe counting lit pixels. Antialiasing makes a bold + and a regular glyph light a similar number, so the metric read "identical" + regardless of the truth. **Text width is the honest measure**: with the + font misconfigured both weights measured 277px, and once corrected they + measured 277px against 306px. + 3. Concluded that Qt or fontconfig was broken, from a bare `QTableView` with + a plain `QStandardItemModel` painting two rows identically. That test was + correct and its conclusion was wrong: the baseline font was already bold, + so `setBold(true)` genuinely changed nothing. + + **The dimming was kept anyway**, and stands on its own merits rather than on + that mistaken diagnosis. `ThreadListModel::readColour()` dims READ rows + toward the background while unread keeps the palette's text colour. With 99 + unread among 4220 read, dimming the bulk carries the list better than + emphasising the few, and it is a second cue that survives a font setting like + the one that caused this. Bold still applies on top. **A caution for anyone adding another `ForegroundRole` cue.** Qt resolves that role into the palette and then prefers it over `HighlightedText`, so a @@ -469,21 +485,78 @@ Two concrete sub-items, from using the list rather than looking at it: delegate is installed view-wide rather than on the subject column alone so every column gets the same handling. - **How this was nearly missed twice.** It was first dismissed from thread - counts, which explained why two screenshots looked alike but said nothing - about rendering. It was then dismissed again by a probe that counted lit - pixels: antialiasing makes a bold and a regular glyph light a similar number, - so the metric read "identical" regardless. Text WIDTH distinguishes them - (277px against 288px for the same string) and a strict pixel diff does; an - ink count does not. The tests that now guard this strip the font from the - model's answer entirely and require the two states to still differ, which is - the assertion that was missing all along. + **One test had to be rewritten when the font was corrected.** + `aSelectedReadThreadIsNotDimmedIntoTheHighlight` originally compared a + selected read row against a selected unread one and required them to paint + identically. That only held because every row was bold; with bold working, + the unread row differs legitimately. It now asserts the resolved palette + rather than pixels, which is the property the fix actually changes. - **A star column for flagged threads**, mirroring the paperclip column that already exists for attachments. `ThreadSummary` carries the tags and `flagged` is an ordinary notmuch tag, so this needs no new worker query, the same way item 15's paperclip did not. Keep it narrow: an icon column, no - text. + text. **Done 2026-08-07**, as `FlagColumn` beside `AttachmentColumn`, using + the same glyph-with-ASCII-fallback pattern (`flagGlyph()`). + +### Built 2026-08-07, and what the layout cost + +The row is roughly doubled in height, with the tags shown as chips beneath the +subject, alternating row colours, and the star column above. + +**The tag strip is painted by the VIEW, not by a delegate**, which is why +`ThreadListView` exists at all. A delegate is handed one cell's rectangle and +cannot paint outside its column, so a strip drawn from the subject column's +delegate stops at that column's edge, losing the last tags of a well-tagged +thread, and starts at that column's left edge, which puts it under the subject +rather than under the row. The user asked for it under the whole row: + +``` +[ date ][ from ][ subject ...................... ] + [ pill ][ pill ][ pill ] +``` + +**Which tags appear.** Everything except `inbox`, `unread`, `flagged`, +`attachment` and the account tag, since the row already shows those as +structure, dimming, the star, the paperclip and the chip. Sorted, because +notmuch's order is not guaranteed stable and a row whose chips reordered +between repaints would flicker. + +**Six defects were introduced and fixed while building this**, every one of +them a consequence of the same thing: a `QTableView` paints PER CELL, and a +row-wide strip is not a cell. Worth listing, because each is easy to +reintroduce. + +1. `SubjectDelegate` was installed view-wide to spread the selection fix + across every column. It reads `AccountLabelRole`, which belongs to the row, + so every column drew the account chip. Split into `RowStyleDelegate` (the + selection fix, every column) and `SubjectDelegate` (chip, subject column + only), with a `Q_ASSERT` guarding the latter. +2. Row height was returned from `sizeHint`, which does nothing: a table takes + ONE height per row, so a hint from a single column applies only if the view + happens to ask that column. Set on the vertical header instead. +3. The strip used `viewportMargins().left()`, which is 0, so it painted from + the viewport edge across the marker columns. It compiled because the method + is protected and the call was inside the subclass. +4. The text band and the strip were measured with one font, so the pills rode + up over the date and sender. +5. Alternating colours and the selection are painted per cell, so the strip's + band showed the bare viewport background as a stripe across every other + row. The view now fills that band itself, and must honour three cases: the + model's own `BackgroundRole` first (a deleted thread's fill would otherwise + be cut in half), then the selection, then the alternating colour. +6. That fill spanned the full width, and the marker glyphs are centred in the + full row height, so its top edge cut the paperclip and star at their + midpoint. The band starts at the date column now. + +**A note on verifying any of this.** Several rendering probes written during +this work returned results that were confidently wrong: counting "lit" pixels +cannot tell bold from regular, since antialiasing lights a similar number +either way, and `viewport()->render()` returned a blank image more than once. +Text width distinguishes weights; a strict pixel diff distinguishes renders; +an ink count distinguishes nothing. Two versions of the strip's own test passed +under mutation before one was written that matched the exact chip colours the +model supplies. ## 6. Opened message stays unread |
