diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-26 19:31:45 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-26 19:31:45 +0200 |
| commit | 59a196ea849c64c51311f56bf62130e74d90b784 (patch) | |
| tree | f322f955b0b5981dfafc85db72f224795941a4ad /src/pendingchangesdialog.h | |
| parent | 1c034f6358f17c5c1d0eeaa04c42c33fac125d93 (diff) | |
| download | qtmaildir-59a196ea849c64c51311f56bf62130e74d90b784.tar.gz qtmaildir-59a196ea849c64c51311f56bf62130e74d90b784.zip | |
feat: open the unsynced-changes count to see what it counts
Item 119, and item 146 which is the same request recorded again. The status
bar's count answers "is my work safe to quit on" and could not say what the
work was.
The label opens a read-only list on a click. A QLabel has no clicked signal,
so the press is taken by MainWindow's existing event filter rather than by
replacing the label with a flat QToolButton, which would have brought the
style's button metrics into a status bar the label already sits correctly
in. The pointing-hand cursor is the affordance, since a status-bar label has
room for nothing else.
The layout is the user's own: a message appears once with its actions
beneath it. PendingChangesDialog::rowsFor() does the grouping over a run of
rows sharing an id, which the snapshot has already ordered, so the actions
under one message keep the order they were made in.
Read-only, deliberately. Retrying or discarding a change from here would be
a new mutation path with its own undo question, and the count exists to be
understood rather than edited.
Three rules the tests pin, each of which is a way the list could disagree
with the count it was opened from:
- Grouping must not collapse: two actions on one message are two rows.
- A thread row stays thread-scoped and reports how many messages it covered.
- An id the index no longer holds still opens a run of its own, showing that
its subject is unknown rather than folding its actions under the message
above it. This is why the row carries startsMessage rather than inferring
it from a non-empty subject.
The queued call carrying QStringList, QList<bool> and QList<int> is covered
by a test that drives it across a real thread, since a container whose
metatype does not resolve is dropped at runtime and the slot runs with a
default. Both survive on Qt 6.11; the test is what says so, and what would
fail if that changed.
Italian ships with it: five new strings, lupdate clean, lrelease 522
finished and 0 unfinished.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P88Q3MCSCSQxKDy7pmXh9F
Diffstat (limited to 'src/pendingchangesdialog.h')
| -rw-r--r-- | src/pendingchangesdialog.h | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/src/pendingchangesdialog.h b/src/pendingchangesdialog.h new file mode 100644 index 0000000..5e47cbd --- /dev/null +++ b/src/pendingchangesdialog.h @@ -0,0 +1,86 @@ +/* + * qtmaildir - a Qt6 mail client for notmuch-indexed Maildirs + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include <QDialog> +#include <QVector> + +#include "types.h" + +/// What one line of the dialog shows. +/// +/// Separate from PendingChange because the two answer different questions. +/// PendingChange is what is outstanding; this is what is drawn, and the +/// difference is the grouping: a message with several actions contributes +/// several rows here, only the first of which carries a subject. +struct PendingChangeRow +{ + /// The subject, drawn only on the first row of a run sharing one id. + /// Empty on the rows beneath it, which is what puts the actions under + /// their message rather than beside a repeated subject. + QString subject; + + /// What the user did. Every row has one; this is the point of the list. + QString action; + + /// True when this row opens a new message, i.e. when `subject` is drawn. + /// Carried explicitly rather than inferred from a non-empty subject: a + /// message whose id no longer resolves has an EMPTY subject and still + /// opens a run of its own. + bool startsMessage = false; + + /// How many messages a thread row covered, or -1 for a message row. + int messageCount = -1; +}; + +/// The list behind the unsynced-changes count (item 119). +/// +/// Read-only, deliberately. This is an information window, not a place to +/// retry or discard a change: either would be a new mutation path with its own +/// undo question, and the count exists to answer "is my work safe to quit on" +/// rather than to be edited. +/// +/// A SNAPSHOT. The rows are built once, when the user opens it, and never +/// refreshed underneath them: a dialog left open for twenty minutes shows what +/// was true when it was opened, which is what the user clicked on. +/// +/// Rows are exposed so the grouping can be asserted without rendering +/// anything, which is how MessageDetailsDialog is tested and for the same +/// reason: a pixel probe cannot tell a correct layout from a plausible one. +class PendingChangesDialog : public QDialog +{ + Q_OBJECT +public: + explicit PendingChangesDialog(const QVector<PendingChange> &changes, + QWidget *parent = nullptr); + + /// The lines on display, in order. Exposed for testing without rendering. + QVector<PendingChangeRow> rows() const { return m_rows; } + + /// Groups the changes into display rows: a subject on the first row of + /// each run sharing an id, the actions beneath it. + /// + /// Static and value-in, value-out so the grouping is testable with no + /// widget at all. + static QVector<PendingChangeRow> rowsFor( + const QVector<PendingChange> &changes); + +private: + QVector<PendingChangeRow> m_rows; +}; |
