diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-03 08:56:33 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-03 08:56:33 +0200 |
| commit | c6c1011222361b446fb3cdee1ad324fe54931abc (patch) | |
| tree | 89aa8a114b327feeb10d83ffec566ec61f24feb3 /src/threadlistmodel.h | |
| parent | 9564794135c3a90fc3988e42233adfd819c5881d (diff) | |
| download | qtmaildir-c6c1011222361b446fb3cdee1ad324fe54931abc.tar.gz qtmaildir-c6c1011222361b446fb3cdee1ad324fe54931abc.zip | |
feat: add ThreadListModel with batch append
QAbstractTableModel over query results, appended in batches so a large
query paints its first screenful immediately. Tag changes apply locally
for optimistic UI; reverting a failed write means calling applyTagChange
again with added and removed swapped, which the round-trip test pins.
Two additions to the drafted version:
- A ThreadIdRole, so a view's QModelIndex maps back to the thread id the
worker speaks without every caller reaching around the model.
- data() checks its own row and column bounds. Qt will not hand out an
out-of-range index and invalidates persistent ones on reset, so this is
unreachable defence rather than a live path; the test says so instead of
pretending to cover it.
Verified by mutation that the empty-batch guard, the ThreadIdRole, and the
full-row dataChanged range each fail exactly one test when removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/threadlistmodel.h')
| -rw-r--r-- | src/threadlistmodel.h | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/threadlistmodel.h b/src/threadlistmodel.h new file mode 100644 index 0000000..2cf8d2e --- /dev/null +++ b/src/threadlistmodel.h @@ -0,0 +1,50 @@ +#pragma once + +#include <QAbstractTableModel> +#include <QVector> + +#include "types.h" + +/// Table model over query results, filled in batches so a large query paints +/// its first screenful immediately. +class ThreadListModel : public QAbstractTableModel +{ + Q_OBJECT +public: + enum Column { + DateColumn = 0, + AuthorsColumn, + SubjectColumn, + TagsColumn, + ColumnCount, + }; + + enum Role { + /// The thread id behind a row. Views hand out QModelIndexes, but the + /// worker speaks thread ids, so the mapping belongs on the model + /// rather than in every caller. + ThreadIdRole = Qt::UserRole + 1, + }; + + explicit ThreadListModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = {}) const override; + int columnCount(const QModelIndex &parent = {}) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role) const override; + + void appendBatch(const QVector<ThreadSummary> &batch); + void clear(); + + ThreadSummary threadAt(int row) const; + + /// Applies a tag change locally so the UI updates before the worker + /// confirms. To revert a failed write, call again with added and removed + /// swapped. + void applyTagChange(const QString &threadId, const QStringList &added, + const QStringList &removed); + +private: + QVector<ThreadSummary> m_threads; +}; |
