presentation/FolderPresentationModel.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "data/Models.h" | ||
| 4 | #include <QAbstractItemModel> | ||
| 5 | #include <QHash> | ||
| 6 | #include <memory> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | // Folder metadata stays in C++; QML receives individual roles, not mailbox data. | ||
| 10 | class FolderPresentationModel final : public QAbstractItemModel { | ||
| 11 | Q_OBJECT | ||
| 12 | public: | ||
| 13 | enum Role { PathRole = Qt::UserRole + 1, NameRole, SelectableRole, UnreadRole, | ||
| 14 | KindRole, ResultCountRole, IconRole, ColorRole }; | ||
| 15 | // Resolved folder appearance: MDI code point and optional #hex tint. | ||
| 16 | struct Appearance { | ||
| 17 | int glyph = 0; | ||
| 18 | QString color; | ||
| 19 | }; | ||
| 20 | 802 | explicit FolderPresentationModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {} | |
| 21 | QModelIndex index(int row, int column = 0, const QModelIndex &parent = {}) const override; | ||
| 22 | QModelIndex parent(const QModelIndex &index) const override; | ||
| 23 | int rowCount(const QModelIndex &parent = {}) const override; | ||
| 24 | int columnCount(const QModelIndex &parent = {}) const override; | ||
| 25 | Qt::ItemFlags flags(const QModelIndex &index) const override; | ||
| 26 | QVariant data(const QModelIndex &index, int role) const override; | ||
| 27 | QHash<int, QByteArray> roleNames() const override; | ||
| 28 | void setFolders(const QList<FolderInfo> &folders, const QStringList &hidden); | ||
| 29 | void setUnread(const QString &path, int unread); | ||
| 30 | // Per-path appearance; paths without an entry (synthetic parents) use | ||
| 31 | // `fallbackGlyph`. Updates rows in place (dataChanged), never resets the tree. | ||
| 32 | void setAppearance(const QHash<QString, Appearance> &byPath, int fallbackGlyph); | ||
| 33 | QStringList branchPaths() const; | ||
| 34 | // Sprint 101 D-32: every folder node in display order (depth first, as | ||
| 35 | // the tree shows them), synthetic parents included. | ||
| 36 | struct TreeRow { | ||
| 37 | QString path; | ||
| 38 | QString name; | ||
| 39 | int depth = 0; | ||
| 40 | }; | ||
| 41 | QList<TreeRow> treeRows() const; | ||
| 42 | bool isSelectable(const QString &path) const; | ||
| 43 | QStringList matchingPaths(const QString &prefix, int limit = 30) const; | ||
| 44 | // Sprint 98 S98.1.9 (D-20): ranked folder candidates of the command bar. | ||
| 45 | // Order: exact leaf name, leaf prefix, start of a path segment, localized | ||
| 46 | // special-folder name ("Gesendet" → Sent), substring; within a match | ||
| 47 | // class the `preferred` paths (predicted move targets, marked suggested) | ||
| 48 | // come first, then `recent` paths, then tree order. Bounded by `limit`. | ||
| 49 | struct Completion { | ||
| 50 | QString path; // raw IMAP path, used for the action | ||
| 51 | QString name; // leaf display name | ||
| 52 | QString parent; // display path of the parent ("" at the top level) | ||
| 53 | int unread = 0; | ||
| 54 | bool suggested = false; | ||
| 55 | }; | ||
| 56 | QList<Completion> completionCandidates(const QString &text, const QStringList &preferred = {}, | ||
| 57 | const QStringList &recent = {}, int limit = 50) const; | ||
| 58 | QString firstSelectable() const; | ||
| 59 | Q_INVOKABLE QModelIndex indexForPath(const QString &path) const; | ||
| 60 |
2/2✓ Branch 2 → 3 taken 23 times.
✓ Branch 3 → 4 taken 23 times.
|
23 | Q_INVOKABLE QString pathForIndex(const QModelIndex &index) const { return data(index, PathRole).toString(); } |
| 61 | private: | ||
| 62 | struct Node { | ||
| 63 | FolderInfo folder; | ||
| 64 | Node *parent = nullptr; | ||
| 65 | QList<Node *> children; | ||
| 66 | int row = 0; | ||
| 67 | int unread = 0; | ||
| 68 | bool synthetic = true; | ||
| 69 | }; | ||
| 70 | Node *node(const QModelIndex &index) const; | ||
| 71 | Node *ensureNode(const QString &path, const QString &delimiter); | ||
| 72 | Node m_root; | ||
| 73 | std::vector<std::unique_ptr<Node>> m_nodes; | ||
| 74 | QHash<QString, Node *> m_paths; | ||
| 75 | QStringList m_selectablePaths; | ||
| 76 | QHash<QString, Appearance> m_appearance; | ||
| 77 | // S98.1.9: lowercase display paths for the command-bar completion, built | ||
| 78 | // on the first completion after setFolders() (one pass instead of one per | ||
| 79 | // keystroke; measured in test_command_completion, SPRINT-98 §13). | ||
| 80 | struct CompletionIndexEntry { | ||
| 81 | const Node *node = nullptr; | ||
| 82 | QString display; // "Projekte/MailJD" | ||
| 83 | QString parent; // "Projekte" | ||
| 84 | QString leafLower; | ||
| 85 | QString displayLower; | ||
| 86 | QString pathLower; | ||
| 87 | QStringList special; // localized special-folder words | ||
| 88 | }; | ||
| 89 | void buildCompletionIndex() const; | ||
| 90 | mutable QList<CompletionIndexEntry> m_completionIndex; | ||
| 91 | mutable bool m_completionIndexValid = false; | ||
| 92 | int m_fallbackGlyph = 0; | ||
| 93 | }; | ||
| 94 |