aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-24 11:47:52 +0200
committerDanilo M. <danix@danix.xyz>2026-08-24 11:47:52 +0200
commita4e8c837a07014be3863a40f371d02a492b7efb7 (patch)
treecc045386b83015c348b982cf46947d63ca2211cb /docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
parent55fe1bca09242220e514394acbddb53be796fe40 (diff)
downloadqtmaildir-a4e8c837a07014be3863a40f371d02a492b7efb7.tar.gz
qtmaildir-a4e8c837a07014be3863a40f371d02a492b7efb7.zip
feat(compose): lay the composer out by scope
Items 142, 143, 144 and 145, to the layout the user described. The composer had one addToolBar carrying three scopes at once: text formatting, message composition, and the terminal action. It read as a menu bar that is not one. There is now no window toolbar at all. From: [.............] +--------+ To: [.........] [v Cc/Bcc] | Send | Subject: [...........................] + [B][I][</>][S][link]["] [Attach] [Send as HTML] +---------------------------------------------+ | message text | +---------------------------------------------+ [Remove] * report.pdf <- only when attached Send is a large icon-above-text button beside the headers: it is the terminal action and carries the weight to match. Formatting is a toolbar widget in the central column directly above the text it formats, icon-only with the words kept as tooltips, which is where a tooltip stops being decoration. Attach and the HTML toggle ride the right end of that bar, past a stretch, because neither formats text. Remove attachment sits with the list it acts on and appears only once something is attached. "Also send a formatted copy" becomes "Send as HTML": the old label described a mechanism without naming it, leaving the reader to infer that "formatted" meant HTML and that "copy" meant a MIME part rather than a second message. Cc and Bcc hide behind a disclosure beside To:. revealCcBccIfUsed() only ever shows, never hides, so nothing but the user's own click can make a field holding an address invisible: a hidden recipient is a message going somewhere the sender cannot see, which is worse than the clutter this removes. The label is hidden with each field, since a QFormLayout holds the two as separate items and hiding the line edit alone strands a "Cc:" over empty space. Two send-lock faults, one predicted and one not. The backlog warned that setInputsEnabled() disabled the single toolbar wholesale, so the send-path test was strengthened to name every control BEFORE the split; it then caught Attach live during a countdown, where a file appended after MessageBuilder has run is either dropped or added to bytes already sent, silently either way. With every control named it failed again on format_bold: disabling a QToolBar greys its buttons but leaves each QAction enabled, so Ctrl+B during a send would have edited a message already being built, through a button that looked unavailable. setInputsEnabled() now walks the bar's actions too. The Italian translation is refreshed; lrelease reports 477 finished, 0 unfinished.
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md105
1 files changed, 105 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
index 3c1c8fd..e5a046e 100644
--- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
+++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
@@ -7215,3 +7215,108 @@ key. A test asserting the resulting 28 alone would stop meaning anything the
moment it did, so it asserts the relation as well.
---
+
+## 142. The composer's formatting buttons share a toolbar with Send and Attach
+
+**Observed.** The user reads the composer's top row as a menu bar that is not
+one, and asks for the formatting controls to move down beside the HTML
+checkbox, directly above the editor.
+
+**Cause, verified 2026-08-23.** `composewindow.cpp:326-397` builds ONE
+`addToolBar`, which carries Bold, Italic, Code, the heading and list actions,
+Link and Quote, then a separator, then Attach, Remove attachment and Send.
+Three different scopes in one row: text formatting, message composition, and
+the terminal action.
+
+**Approach.** Split it. The formatting half moves to a row directly above the
+editor, where the text it formats is; Attach, Remove attachment and Send stay
+in the window's own toolbar. The HTML checkbox already sits under the editor
+and is the anchor the user names.
+
+**Constraints.** `setInputsEnabled()` disables `m_formatToolbar` wholesale
+during a send (`composewindow.cpp:692`), so a split needs both halves disabled,
+and a test for the send path that asserts on only one of them would pass
+against a live Attach button during a send.
+
+**Outcome, and the constraint was right twice over.** The existing
+`disablingInputsCoversEveryFieldAndTheToolbar` did assert on the toolbar as
+one widget, exactly as predicted, so it was strengthened to name every
+control BEFORE the split. It then failed on the first build, reporting
+`compose_attach` live during a countdown, which is the fault the entry
+describes: a file appended to `m_attachments` after `MessageBuilder` has run
+is either silently dropped or added to bytes already handed to the send
+command, and neither reports anything.
+
+**The second fault was not predicted and is worth more than the first.**
+With every control named, the test then failed on `format_bold`. Disabling a
+`QToolBar` greys its buttons but leaves each `QAction` ENABLED, so the
+keyboard shortcut still fires: `Ctrl+B` during a send would have edited a
+message already being built, through a button that looked unavailable. The
+old code was never wrong about this only because the whole row was one
+widget whose actions nothing else could reach. `setInputsEnabled()` now walks
+`m_formatToolbar->actions()` as well as disabling the bar.
+
+---
+
+## 143. The formatting buttons are text where every editor uses icons
+
+**Observed.** The user asks for icon-only formatting buttons, "like any other
+text editor".
+
+**Cause.** They were built as text actions, and the composer's toolbar has no
+icons at all.
+
+**Approach.** `QIcon::fromTheme` per CLAUDE.md's rule that chrome is the
+system's, keeping the current text as the tooltip so nothing becomes
+unnameable. Cheap once item 142 has moved the row, and awkward before, since
+the same row would then mix icon-only formatting with text Send and Attach.
+
+**Constraints.** `format-text-bold` and its siblings are standard freedesktop
+names, but a theme may not carry all of them; an action with no icon must fall
+back to its text rather than rendering as an empty button. Icon-only is also
+the state where the tooltip stops being decoration, so every one needs to be
+right.
+
+---
+
+## 144. "Also send a formatted copy" is prominent and says nothing
+
+**Observed.** The user's note: "I suppose it means 'format/send as html', but
+that flag is secondary, doesn't need to be so prominent."
+
+**Cause, verified 2026-08-23.** `composewindow.cpp:265`. The label describes a
+mechanism ("a formatted copy") without naming it, so the reader has to infer
+that "formatted" means HTML and that "copy" means an additional MIME part
+rather than a second message.
+
+**Approach.** Two independent halves, and they can ship separately. Say what it
+does: something closer to "Send an HTML version as well". And demote it: it is
+a per-message override of a config default (`[compose] send_html`), which is
+secondary to writing the message.
+
+**Constraints.** The string is translated, so changing it makes the Italian
+entry stale; `ctest -R translations` fails on an untranslated string, which is
+the intended safety net rather than an obstacle. Nothing matches on this text,
+so CLAUDE.md's "translating a string something matches on" trap does not apply.
+
+---
+
+## 145. Cc and Bcc are permanent rows on every composer
+
+**Observed.** The user asks for them hidden behind a disclosure next to the To:
+field.
+
+**Cause, verified 2026-08-23.** `composewindow.cpp:251` and `255` add both as
+unconditional form rows. Most messages address neither, so two of the four
+header rows are usually empty.
+
+**Approach.** A disclosure beside To: that reveals both together. They expand
+automatically, and stay expanded, whenever either already carries a value: a
+reply that carries Cc, or a reopened draft, must not hide a recipient the
+message is actually addressed to.
+
+**Constraints.** That auto-expansion is the load-bearing half. A hidden field
+holding an address is a message going somewhere the sender cannot see, which is
+worse than the clutter this removes. The seeding runs before `buildUi()`'s
+`markDirty()` connections per the constructor's ordering comment, so whatever
+decides the initial state has to read the seeded values rather than the widgets.