summaryrefslogtreecommitdiffstats
path: root/src/mainwindow.h
AgeCommit message (Collapse)AuthorFilesLines
5 hoursfix(query): let the query bar keep Return instead of open_threadDanilo M.1-0/+5
Pressing Return in the query bar moved focus to the thread list and left the query unrun, so a query typed at the keyboard could not be executed at all. Return is bound to open_thread as a Qt::WindowShortcut, and a shortcut is dispatched before the focused widget ever sees the key. Qt withholds a plain-LETTER shortcut from an editable widget, which is why every other binding in the map was safe here, but Return is not a letter and gets no such protection: the action fired from inside the bar, its handler called setFocus() on the thread list, and QLineEdit::returnPressed was never emitted. Accept the ShortcutOverride for Return and Enter on the query bar, which tells Qt the focused widget wants the key as ordinary input and stops the shortcut being dispatched. Narrow by design, one widget and one key, so open_thread keeps working everywhere else in the window. Three earlier hypotheses were tested and disproven before this one, and two synthetic probes wrongly reported the binding as harmless: real input sends ShortcutOverride first and only dispatches the shortcut if nothing claims it, while QTest::keyClick skips that round trip entirely. The new test drives the override exchange rather than the keystroke and fails against the old code. The comment claiming no filter was needed said the letter rule covered this case; it did not, and it now says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5 hoursfeat(completion): wire query completion into the main windowDanilo M.1-0/+12
Adds the complete_query action, whose registration the assertion in registerActions() has been demanding since keymap gained the name: a known action nothing implements would have been a silently dead binding. Also lands the QueryCompleter half of the manual trigger, which the keymap commit left behind: triggerCompletion() and the focus-in filter gated on completion_on_focus. Tags refresh at startup, after a sync, and after a mutation introduces a tag not already known, since that is the tag most likely to be typed again. onAllTagsReady deliberately drops the generation the signal carries: a tag list is not an ordered query result, so a later one is always at least as good as an earlier one and discarding on staleness could only throw away a good list.
5 hoursfeat: persist window, splitter and column widthsDanilo M.1-0/+17
Resizing the window, the splitter or a thread-list column was undone by the next launch. State now round-trips through a separate settings file at ~/.local/state/qtmaildir/uistate.conf, written on close and read at startup. The state file is deliberately not the user's config: a base64 geometry blob does not belong in a hand-edited file, and rewriting that file on exit would drop its comments and key order, which QSettings does not preserve. Two details that are easy to get wrong: QStandardPaths::StateLocation appends both the organization and the application name, and both are "qtmaildir" here, so it resolves to ~/.local/state/qtmaildir/qtmaildir. The path is built from GenericStateLocation instead, matching Config::defaultPath(). Restore runs after buildMenus() rather than at the end of buildUi(): QMainWindow::restoreState() matches toolbars by object name and silently drops the position of one that does not exist yet. Every restore is conditional on a non-empty blob, so a missing or rejected state file leaves the built-in defaults instead of producing a zero-size window. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5 hoursfeat: let an account set its chip colour and labelDanilo M.1-0/+2
Two optional keys in an [account.<key>] stanza. color fills the chip, label sets its text. Both belong to the account rather than to [tagcolors] because an account tag is a different taxonomy: which mailbox a thread arrived in, not what state it is in. label is display only. "account-provider-work" is a lot of row for one bit of information, but the notmuch tag is never renamed, so existing queries and external tagging are unaffected. Unset falls back to the account key, and an empty label is ignored rather than rendering a blank chip.
6 hoursfeat: add menus, a toolbar and a shortcut referenceDanilo M.1-8/+23
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.
6 hoursAdd GPLv2-only license and per-file headersDanilo M.1-0/+18
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>
6 hoursfeat: add MainWindow wiring query, list, message, and syncDanilo M.1-0/+148
Wires the worker thread, thread list, message pane, sync process and undo stack together, and replaces the placeholder main() with real startup: custom URL schemes registered before QApplication, a libnotmuch ABI check, and config loading. Four fixes against the drafted version: - onWorkerError() only set a status label. Its own comment elsewhere claimed it reverted the optimistic update, and the spec requires that; it did not, so a rejected write left the list showing a tag the database never received. The pending change is now recorded and rolled back, and a confirmed tagsApplied clears it so a later unrelated error cannot undo a write that succeeded. - runCurrentQuery() cleared the model but left the undo stack pointing at rows that no longer exist. Undoing after a new query would have written to the database while the visible list stayed put. The stack is cleared with the model. - m_currentMessages was assigned on every thread load and never read. Removed. - buildUi() connected sync output to m_syncLog and errors to m_statusLabel before either existed. Both are constructed before the wiring now. cidPrefix generation lives here, this being its only producer in the application, and is pinned by tests: it must never contain '!' and must be distinct per message, which are the invariants the cid: namespacing rests on. A second test holds registeredActionNames() against KeyMap::knownActions(), since those two hand-maintained lists drifting either way silently breaks a user's key binding. Mutation-verified that dropping an action fails the test by name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>