presentation/MailSearchSessions.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "MailSearchSession.h" | ||
| 4 | #include "SearchTimelineModel.h" | ||
| 5 | #include <QObject> | ||
| 6 | #include <QStringList> | ||
| 7 | #include <functional> | ||
| 8 | #include <map> | ||
| 9 | #include <memory> | ||
| 10 | |||
| 11 | class MailController; | ||
| 12 | class WorkspaceState; | ||
| 13 | |||
| 14 | // Sprint 101 S101.1.2/S101.3.x (D-2, D-3, D-12, D-29): the search sessions | ||
| 15 | // of the search tabs. One session per search tab, created when the tab is | ||
| 16 | // first shown (restored tabs re-run lazily, D-16) and destroyed with the | ||
| 17 | // tab. It keeps at most MaximumLiveSessions sessions with rows in memory, | ||
| 18 | // gives the one search connection to the active tab, keeps the rows in | ||
| 19 | // step with mail actions and moves the selection to the first result or | ||
| 20 | // the successor when the selected result leaves the list. | ||
| 21 | // | ||
| 22 | // Owner: QuickMailBackend. GUI thread only. QML reads `active`. | ||
| 23 | class MailSearchSessions final : public QObject { | ||
| 24 | Q_OBJECT | ||
| 25 | Q_PROPERTY(MailSearchSession *active READ active NOTIFY activeChanged) | ||
| 26 | Q_PROPERTY(bool activeDormant READ activeDormant NOTIFY activeChanged) | ||
| 27 | Q_PROPERTY(QString activeAccount READ activeAccount NOTIFY activeChanged) | ||
| 28 | // D-26: the timeline band of every search tab ("View ▾ → Timeline"). | ||
| 29 | Q_PROPERTY(bool timelineShown READ timelineShown WRITE setTimelineShown NOTIFY timelineShownChanged) | ||
| 30 | Q_PROPERTY(SearchTimelineModel *timeline READ timeline CONSTANT) | ||
| 31 | // D-32: the sidebar of a search tab shows the facets; true brings the | ||
| 32 | // full folder tree back (with its usual folder clicks). | ||
| 33 | Q_PROPERTY(bool allFolders READ allFolders WRITE setAllFolders NOTIFY allFoldersChanged) | ||
| 34 | // S101.5.9 (D-22): the result list grouped by the timeline's unit. | ||
| 35 | Q_PROPERTY(bool grouped READ grouped WRITE setGrouped NOTIFY groupedChanged) | ||
| 36 | // D-30: the one-time "click a bar to zoom in" hint was shown. | ||
| 37 | Q_PROPERTY(bool timelineHintSeen READ timelineHintSeen WRITE setTimelineHintSeen NOTIFY timelineHintSeenChanged) | ||
| 38 | |||
| 39 | public: | ||
| 40 | struct Deps { | ||
| 41 | WorkspaceState *workspace = nullptr; | ||
| 42 | MailController *controller = nullptr; | ||
| 43 | // The session dependencies for an account (cache, reader, admission, …). | ||
| 44 | std::function<MailSearchSession::Deps(const QString &accountId)> sessionDeps; | ||
| 45 | std::function<QString()> currentAccount; | ||
| 46 | std::function<bool(const MailId &)> acceptIdentity; | ||
| 47 | }; | ||
| 48 | static constexpr int MaximumLiveSessions = 3; | ||
| 49 | |||
| 50 | MailSearchSessions(Deps deps, QObject *parent = nullptr); | ||
| 51 | ~MailSearchSessions() override; | ||
| 52 | |||
| 53 | MailSearchSession *active() const; | ||
| 54 | bool activeDormant() const; | ||
| 55 | QString activeAccount() const; | ||
| 56 | MailSearchSession *session(const QString &tabId) const; | ||
| 57 | // S101.1.10: `text` escaped for StyledText with the active search's | ||
| 58 | // free-text words (folded like the index) wrapped in open/close. | ||
| 59 | Q_INVOKABLE QString markMatches(const QString &text, const QString &open, const QString &close) const; | ||
| 60 | 2 | int sessionCount() const { return int(m_sessions.size()); } | |
| 61 | int liveCount() const; | ||
| 62 | 4129 | bool timelineShown() const { return m_timelineShown; } | |
| 63 | 110 | SearchTimelineModel *timeline() { return &m_timeline; } | |
| 64 | 94 | bool timelineHintSeen() const { return m_timelineHintSeen; } | |
| 65 | 112 | bool allFolders() const { return m_allFolders; } | |
| 66 | 102 | bool grouped() const { return m_grouped; } | |
| 67 | void setGrouped(bool on); | ||
| 68 | void setAllFolders(bool on); | ||
| 69 | void setTimelineHintSeen(bool seen); | ||
| 70 | void setTimelineShown(bool shown); | ||
| 71 | |||
| 72 | // D-1/D-2: a search in the search tab of the current account — the most | ||
| 73 | // recently used one unless `newTab`; an empty query opens an empty tab. | ||
| 74 | // False at the tab limit or without an account. | ||
| 75 | bool open(const QString &query, bool newTab); | ||
| 76 | void refreshAfterIndexReady(); | ||
| 77 | |||
| 78 | signals: | ||
| 79 | void activeChanged(); | ||
| 80 | void timelineShownChanged(); | ||
| 81 | void timelineHintSeenChanged(); | ||
| 82 | void allFoldersChanged(); | ||
| 83 | void groupedChanged(); | ||
| 84 | // The active search tab moved its selection (the view scrolls to it). | ||
| 85 | void selectionRevealRequested(int row); | ||
| 86 | |||
| 87 | private: | ||
| 88 | MailSearchSession *ensure(const QString &tabId); | ||
| 89 | void syncActive(); | ||
| 90 | void touch(const QString &tabId); | ||
| 91 | void selectRow(MailSearchSession *session, int row); | ||
| 92 | void onRowsReplaced(MailSearchSession *session, const MailId &selected); | ||
| 93 | void onMovesPending(qint64 folderId, const QList<qint64> &uids); | ||
| 94 | MailSearchSession *serverOwner() const; | ||
| 95 | void wireController(); | ||
| 96 | void updatePeriods(); | ||
| 97 | void updateMatchWords(MailSearchSession *session); | ||
| 98 | |||
| 99 | Deps m_deps; | ||
| 100 | std::map<QString, std::unique_ptr<MailSearchSession>> m_sessions; | ||
| 101 | QStringList m_recent; // most recently active first (D-3) | ||
| 102 | // Folded free-text words per tab, shared with the tab's match provider. | ||
| 103 | std::map<QString, std::shared_ptr<QStringList>> m_matchWords; | ||
| 104 | QString m_activeTabId; | ||
| 105 | bool m_timelineShown = true; | ||
| 106 | bool m_timelineHintSeen = false; | ||
| 107 | bool m_allFolders = false; | ||
| 108 | bool m_grouped = false; | ||
| 109 | SearchTimelineModel m_timeline; | ||
| 110 | }; | ||
| 111 |