presentation/QuickSettingsNavigation.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | #include <QHash> | ||
| 3 | #include <QObject> | ||
| 4 | #include <QStringList> | ||
| 5 | #include <QVariantMap> | ||
| 6 | #include <functional> | ||
| 7 | |||
| 8 | class WorkspaceState; | ||
| 9 | |||
| 10 | // Sprint 100 S100.1.2/S100.1.3 (D-1, D-3, D-11): where the settings area is | ||
| 11 | // and how one leaves it. Targets are ids, never indices (rule 9): | ||
| 12 | // "" the page visited last (Accounts the first time) | ||
| 13 | // "<page>" a page of SettingsCatalog | ||
| 14 | // "<page>/<group|setting>" a page, scrolled to that place and highlighted | ||
| 15 | // "accounts/new" the guided setup (param "services=dav" for DAV only) | ||
| 16 | // "accounts/<accountId>[/mail|rules|calendars|password|remove]" | ||
| 17 | // "accounts/<catalog setting>" the same for the active account | ||
| 18 | // Unknown targets open the page visited last and log a warning | ||
| 19 | // (mailjd.settings). The dirty registry is the one guard of every settings | ||
| 20 | // editor: leaving the page or the area, opening another editor and closing | ||
| 21 | // the window ask once ("Save · Discard · Keep editing") — through the | ||
| 22 | // shell (request(), guard()); open() is the unguarded step behind them. | ||
| 23 | class QuickSettingsNavigation final : public QObject { | ||
| 24 | Q_OBJECT | ||
| 25 | Q_PROPERTY(bool isOpen READ isOpen NOTIFY changed) | ||
| 26 | // A catalog page id, or "account" for the page of one account. | ||
| 27 | Q_PROPERTY(QString page READ page NOTIFY changed) | ||
| 28 | Q_PROPERTY(QString accountId READ accountId NOTIFY changed) | ||
| 29 | // What the account page opens: "", "mail", "rules", "calendars", | ||
| 30 | // "password", "remove"; on "accounts": "new". | ||
| 31 | Q_PROPERTY(QString section READ section NOTIFY changed) | ||
| 32 | // The place to reveal and highlight ("privacy/remote-images") or a field | ||
| 33 | // name of the opened editor ("password"). | ||
| 34 | Q_PROPERTY(QString anchor READ anchor NOTIFY changed) | ||
| 35 | Q_PROPERTY(QVariantMap params READ params NOTIFY changed) | ||
| 36 | // Increments on every open(), so opening the same place again re-reveals it. | ||
| 37 | Q_PROPERTY(int revision READ revision NOTIFY changed) | ||
| 38 | Q_PROPERTY(QString backArea READ backArea NOTIFY changed) | ||
| 39 | Q_PROPERTY(QString lastPage READ lastPage NOTIFY changed) | ||
| 40 | Q_PROPERTY(QStringList attention READ attention NOTIFY attentionChanged) | ||
| 41 | Q_PROPERTY(bool dirty READ dirty NOTIFY dirtyChanged) | ||
| 42 | Q_PROPERTY(QString dirtyEditor READ dirtyEditor NOTIFY dirtyChanged) | ||
| 43 | // The guard question is open; `pendingAction` waits for the answer. | ||
| 44 | Q_PROPERTY(bool guardOpen READ guardOpen NOTIFY guardChanged) | ||
| 45 | Q_PROPERTY(QString pendingAction READ pendingAction NOTIFY guardChanged) | ||
| 46 | public: | ||
| 47 | struct Target { | ||
| 48 | QString page, accountId, section, anchor; | ||
| 49 | QVariantMap params; | ||
| 50 | bool known = true; | ||
| 51 | }; | ||
| 52 | explicit QuickSettingsNavigation(WorkspaceState *workspace, QObject *parent = nullptr); | ||
| 53 | // The account "accounts/password" and friends act on. | ||
| 54 | 766 | void setActiveAccountResolver(std::function<QString()> resolver) { m_activeAccount = std::move(resolver); } | |
| 55 | // Pure parser of the target grammar above. | ||
| 56 | static Target parse(const QString &target, const QString &activeAccount); | ||
| 57 | |||
| 58 | 716 | bool isOpen() const { return m_open; } | |
| 59 | 33019 | QString page() const { return m_target.page; } | |
| 60 | 802 | QString accountId() const { return m_target.accountId; } | |
| 61 | 364 | QString section() const { return m_target.section; } | |
| 62 | 327 | QString anchor() const { return m_target.anchor; } | |
| 63 | 74 | QVariantMap params() const { return m_target.params; } | |
| 64 | 2 | int revision() const { return m_revision; } | |
| 65 | 1356 | QString backArea() const { return m_backArea; } | |
| 66 | ✗ | QString lastPage() const { return m_lastPage; } | |
| 67 | 9142 | QStringList attention() const { return m_attention; } | |
| 68 | bool dirty() const; | ||
| 69 | QString dirtyEditor() const; | ||
| 70 | 69 | bool guardOpen() const { return m_guardOpen; } | |
| 71 | 2 | QString pendingAction() const { return m_pendingAction; } | |
| 72 | |||
| 73 | // Opens the area on a target; switches the workspace area to "settings" | ||
| 74 | // and remembers the area to return to. Returns false for an unknown | ||
| 75 | // target (the area still opens on the last page). | ||
| 76 | Q_INVOKABLE bool open(const QString &target = {}, const QString &anchor = {}); | ||
| 77 | // From views outside the area (empty states, banners) and from clicks | ||
| 78 | // inside it: asks the shell to open the target, so the shell's rules | ||
| 79 | // (composer, the dirty guard) apply first. open() itself never asks | ||
| 80 | // (S106-330); it is for the shell and for programmatic moves after a save. | ||
| 81 | Q_INVOKABLE void request(const QString &target, const QString &anchor = {}); | ||
| 82 | // Returns to the area the settings were opened from. The caller asks the | ||
| 83 | // guard first (guard("close")). | ||
| 84 | Q_INVOKABLE void close(); | ||
| 85 | // Editors report their state; `label` names the editor in the question. | ||
| 86 | Q_INVOKABLE void setEditorDirty(const QString &editorId, const QString &label, bool dirty); | ||
| 87 | Q_INVOKABLE void removeEditor(const QString &editorId); | ||
| 88 | // True when nothing is dirty: the caller may go on. Otherwise the question | ||
| 89 | // opens and `action` waits ("close", "open:<target>", "area:<area>", "quit", | ||
| 90 | // "editor:<id>"); answerGuard("save"|"discard"|"keep") resolves it. | ||
| 91 | Q_INVOKABLE bool guard(const QString &action); | ||
| 92 | Q_INVOKABLE void answerGuard(const QString &choice); | ||
| 93 | // An editor could not save after "Save" in the guard: the waiting action | ||
| 94 | // is dropped and the editor stays open with its error. | ||
| 95 | Q_INVOKABLE void saveFailed(); | ||
| 96 | void setAttention(const QStringList &places); | ||
| 97 | |||
| 98 | signals: | ||
| 99 | void changed(); | ||
| 100 | void attentionChanged(); | ||
| 101 | void dirtyChanged(); | ||
| 102 | void guardChanged(); | ||
| 103 | // To every registered editor: save, or drop the draft. | ||
| 104 | void saveRequested(); | ||
| 105 | void discardRequested(); | ||
| 106 | // The guard is resolved; the shell performs the waiting action. | ||
| 107 | void proceed(const QString &action); | ||
| 108 | void openRequested(const QString &target, const QString &anchor); | ||
| 109 | |||
| 110 | private: | ||
| 111 | void finishGuard(bool go); | ||
| 112 | WorkspaceState *m_workspace; | ||
| 113 | std::function<QString()> m_activeAccount; | ||
| 114 | Target m_target; | ||
| 115 | bool m_open = false; | ||
| 116 | int m_revision = 0; | ||
| 117 | QString m_backArea = QStringLiteral("mail"); | ||
| 118 | QString m_lastPage; | ||
| 119 | QStringList m_attention; | ||
| 120 | struct Editor { QString label; bool dirty = false; }; | ||
| 121 | QHash<QString, Editor> m_editors; | ||
| 122 | QStringList m_editorOrder; | ||
| 123 | bool m_guardOpen = false; | ||
| 124 | bool m_saving = false; | ||
| 125 | QString m_pendingAction; | ||
| 126 | }; | ||
| 127 |