| Age | Commit message (Collapse) | Author | Files | Lines |
|
MimeParser has filled To and Cc all along and HtmlBuilder simply never
interpolated them, so both were parsed on every message and then
discarded. The header strip showed the subject and a message count and
nothing else, which is item 2 of the usability backlog.
The header now adapts to what it can say honestly. A thread holding one
message shows From, To and Cc under the subject, where every field is
unambiguous. A thread holding several keeps showing the subject and the
count alone: the recipient differs message to message, and once the user
has replied there is no single address the thread is addressed to, so
naming one would be a guess presented as a fact. Per-message detail is
what the dialog is for.
That dialog lists Subject, From, To, Cc, Date and Message-Id for every
message, numbered when there is more than one, in a read-only plain-text
widget. Plain text is the security decision, not a stylistic one: these
values come from strangers and the dialog exists to show them verbatim,
so the format that cannot interpret markup is the right one. The header
label is RichText and every value interpolated into it is escaped, since
an unescaped From injects into the application's own chrome rather than
into the sandboxed page.
Reached by a Details... button beside the subject and by Ctrl+Shift+D.
Both, because a shortcut alone restates the complaint this backlog opened
with. The binding is shifted because Ctrl+D is delete, and the
destructive action keeps the key it already had rather than being moved
to make room.
An empty Cc omits its row instead of printing a label with nothing after
it. Both header shapes were rendered to PNG and inspected, not only
asserted.
The new button also exposed a latent flaw in an older test:
attachmentButtonLabels() identified attachment buttons by excluding the
one other button's label, so it counted the details button as an
attachment as soon as one existed. It now finds the bar by object name
and reads only its children.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
KeyMap::defaultBindings() is the single source of truth for shortcuts:
the menus, the shortcut reference dialog and loadDefaults() all read it,
so registering here makes the binding appear in the reference and stay
rebindable from [keys] without any of them disagreeing.
Ctrl+Space is a modifier plus a named key, so it sidesteps the bare
capital trap in normalizeSequence() and needs no Shift on any layout.
Verified it parses to a single non-empty combination that round-trips to
"Ctrl+Space", and it collides with no existing default.
Note test_mainwindow now fails its everyKnownActionIsRegistered()
assertion: MainWindow does not yet implement complete_query. That wiring
is a separate task, and the assertion firing is the intended signal.
|
|
Zoom was Chromium's, not the application's: the web view handled the
keys natively and never told anyone, so there was no value to save.
qtmaildir now owns it. Zoom in, out and reset are real actions, in the
View menu and rebindable through [keys], and the factor is persisted to
the UI state file. Ctrl+wheel over the body zooms and Ctrl+middle-click
resets, both filtered by ancestry from an application-level filter: the
events are delivered to an internal QQuickWidget the web view creates
lazily, so a filter on the view itself never sees them.
The factor is clamped to 0.5 - 3.0, and NaN, infinity, zero and negative
values fall back to 1.0, since a corrupt state file must not be able to
leave the pane unreadable with no visible way back.
Both risks the plan flagged turned out not to exist, verified by probe
rather than assumed. The application QAction wins over the web view's
native zoom key, so the tracked factor cannot diverge from what is on
screen. And the factor survives setHtml(), so the web view is the single
source of truth and needs no reapply per render.
A third finding is worth recording because it produced a wrong fix
first. A probe using QTest::keyClick() reported Ctrl++ as a dead
binding, and a test was written asserting that. Both were wrong: Ctrl++
is exactly what the '+' key emits on an Italian layout, confirmed
against the real keyboard, and it is the shipped default. Whether a
symbol needs Shift is a property of the layout, not of Qt, and
keyClick() reproduces neither. The test now only checks that every
default parses, and the comment in defaultBindings() says not to
re-derive this from synthetic input.
Ctrl+= is a second binding for reset, skipped when [keys] gives it to
something else.
Also fixes a pre-existing bug the new config key exposed. [general]
entries were read as "general/<key>", which matches nothing: QSettings'
INI backend treats a section literally named [general] as its own
fallback section and strips the prefix. notmuch_config had therefore
never worked. Both keys are now read without it; the file format is
unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Actions were a QHash of std::function dispatched by an event filter,
which nothing could put in a menu. They are QActions now, bound from
KeyMap so a [keys] override reaches the menus as well as the keyboard.
Menu bar covers every action; the toolbar carries only Sync, Archive,
Delete and Undo. Help > Keyboard shortcuts is generated from the actions,
so it shows what the keys really do rather than a copy that drifts.
spam and load_remote gained defaults, having been unreachable without a
hand-written binding.
The event filter is gone. Probing showed QAction shortcuts are dispatched
before the focused widget sees the key, so they beat QAbstractItemView's
type-to-search without one, and Qt already suppresses plain-letter
shortcuts while an editable widget has focus. Dropping the filter's
blanket guard also lets Ctrl+Q work while the query bar has focus.
registeredActionNames() is derived from the actions rather than
hand-maintained, so the two drift tests it needed are replaced by checks
that a configured binding reaches its action.
No confirmation dialogs: tag mutations still answer to undo.
|
|
Typing a capital emits Shift+<key>, but QKeySequence::fromString() folds
the case of a bare letter away: "N" parsed to plain Key_N, a combination
no keystroke produces. The N, F and G defaults (toggle_unread, flag and
sync) therefore never fired, and neither would any hand-written capital
in [keys].
normalizeSequence() rewrites a bare capital to Shift+<letter> and is
shared by the defaults and the override pass. As a side effect "y" and
"Y" become distinct keys rather than a collision that dropped one.
Defaults move to modifier shortcuts throughout. A single letter cannot
be a QAction shortcut without stealing that letter from every text field
in the window, and the menus in the next commit need real accelerators.
defaultBindings() is now the one source of truth for them.
|
|
Confirmed with the maintainer as v2-only rather than v2-or-later. LICENSE is
the official text from gnu.org. Every file under src/ and tests/ carries the
matching notice.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
loadOverrides() inserted straight into m_bindings, so two override
lines that normalize to the same QKeySequence (e.g. "y" and "Y", both
"Y" per QKeySequence) silently overwrote each other with zero warning,
contradicting the "a typo cannot bind silently" contract on
knownActions().
Track sequences seen within the current override pass separately from
m_bindings (which already holds the defaults) so overriding a default
key stays silent, but two colliding override lines produce one warning
naming both actions.
|
|
Maps key sequences to action name strings, with hardcoded vim-style
defaults and QSettings-based [keys] overrides. Unknown actions and
unparseable sequences are collected as warnings rather than treated
as fatal, so a typo in the config cannot silently misbind or crash.
Note: QKeySequence::fromString() on Qt 6.11 does not return an empty
sequence for unparseable input (e.g. "NotAKey++") -- it returns a
non-empty sequence whose toString() is empty. Detection uses that
instead of isEmpty().
|