1 | /* |
2 | SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net> |
3 | SPDX-FileContributor: Stephen Kelly <stephen@kdab.com> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #ifndef KVIEWSTATESERIALIZER_H |
9 | #define KVIEWSTATESERIALIZER_H |
10 | |
11 | #include <QObject> |
12 | #include <QPair> |
13 | #include <QStringList> |
14 | #include <memory> |
15 | |
16 | #include "kwidgetsaddons_export.h" |
17 | |
18 | class QAbstractItemView; |
19 | class QItemSelectionModel; |
20 | class QAbstractItemModel; |
21 | class QAbstractScrollArea; |
22 | class QModelIndex; |
23 | |
24 | class KViewStateSerializerPrivate; |
25 | |
26 | /** |
27 | @class KViewStateSerializer kviewstateserializer.h KViewStateSerializer |
28 | |
29 | @brief Object for saving and restoring state in QTreeViews and QItemSelectionModels |
30 | |
31 | Implement the indexFromConfigString and indexToConfigString methods to handle |
32 | the model in the view whose state is being saved. These implementations can be |
33 | quite trivial: |
34 | |
35 | @code |
36 | QModelIndex DynamicTreeStateSaver::indexFromConfigString(const QAbstractItemModel* model, |
37 | const QString& key) const |
38 | { |
39 | QModelIndexList list = model->match(model->index(0, 0), |
40 | DynamicTreeModel::DynamicTreeModelId, |
41 | key.toInt(), 1, Qt::MatchRecursive); |
42 | if (list.isEmpty()) { |
43 | return QModelIndex(); |
44 | } |
45 | return list.first(); |
46 | } |
47 | |
48 | QString DynamicTreeStateSaver::indexToConfigString(const QModelIndex& index) const |
49 | { |
50 | return index.data(DynamicTreeModel::DynamicTreeModelId).toString(); |
51 | } |
52 | @endcode |
53 | |
54 | It is possible to restore the state of a QTreeView (that is, the expanded state and |
55 | selected state of all indexes as well as the horizontal and vertical scroll state) by |
56 | using setTreeView. |
57 | |
58 | If there is no tree view state to restore (for example if using QML), the selection |
59 | state of a QItemSelectionModel can be saved or restored instead. |
60 | |
61 | The state of any QAbstractScrollArea can also be saved and restored. |
62 | |
63 | A KViewStateSerializer should be created on the stack when saving and on the heap |
64 | when restoring. The model may be populated dynamically between several event loops, |
65 | so it may not be immediate for the indexes that should be selected to be in the model. |
66 | The saver should *not* be persisted as a member. The saver will destroy itself when it |
67 | has completed the restoration specified in the config group, or a small amount of time |
68 | has elapsed. |
69 | |
70 | @code |
71 | MyWidget::MyWidget(Qobject *parent) |
72 | : QWidget(parent) |
73 | { |
74 | ... |
75 | |
76 | m_view = new QTreeView(splitter); |
77 | m_view->setModel(model); |
78 | |
79 | connect(model, &QAbstractItemModel::modelAboutToBeReset, this, [this]() { saveState(); }); |
80 | connect(model, &QAbstractItemModel::modelReset, [this]() { restoreState(); }); |
81 | connect(qApp, &QApplication::aboutToQuit, this, [this]() { saveState(); }); |
82 | |
83 | restoreState(); |
84 | } |
85 | |
86 | void StateSaverWidget::saveState() |
87 | { |
88 | ConcreteStateSaver saver; |
89 | saver.setTreeView(m_view); |
90 | |
91 | KConfigGroup cfg(KSharedConfig::openConfig(), "ExampleViewState"); |
92 | saver.saveState(cfg); |
93 | cfg.sync(); |
94 | } |
95 | |
96 | void StateSaverWidget::restoreState() |
97 | { |
98 | // Will delete itself. |
99 | ConcreteTreeStateSaver *saver = new ConcreteStateSaver(); |
100 | saver->setTreeView(m_view); |
101 | KConfigGroup cfg(KSharedConfig::openConfig(), "ExampleViewState"); |
102 | saver->restoreState(cfg); |
103 | } |
104 | @endcode |
105 | |
106 | After creating a saver, the state can be saved using a KConfigGroup. |
107 | |
108 | It is also possible to save and restore state directly by using the restoreSelection, |
109 | restoreExpanded etc methods. Note that the implementation of these methods should return |
110 | strings that the indexFromConfigString implementation can handle. |
111 | |
112 | @code |
113 | class DynamicTreeStateSaver : public KViewStateSerializer |
114 | { |
115 | Q_OBJECT |
116 | public: |
117 | // ... |
118 | |
119 | void selectItems(const QList<qint64> &items) |
120 | { |
121 | QStringList itemStrings; |
122 | for (qint64 item : items) { |
123 | itemStrings << QString::number(item); |
124 | } |
125 | restoreSelection(itemStrings); |
126 | } |
127 | |
128 | void expandItems(const QList<qint64> &items) |
129 | { |
130 | QStringList itemStrings; |
131 | for (qint64 item : items) { |
132 | itemStrings << QString::number(item); |
133 | } |
134 | restoreSelection(itemStrings); |
135 | } |
136 | }; |
137 | @endcode |
138 | |
139 | Note that a single instance of this class should be used with only one widget. |
140 | That is don't do this: |
141 | |
142 | @code |
143 | saver->setTreeView(treeView1); |
144 | saver->setSelectionModel(treeView2->selectionModel()); |
145 | saver->setScrollArea(treeView3); |
146 | @endcode |
147 | |
148 | To save the state of 3 different widgets, use three savers, even if they operate |
149 | on the same root model. |
150 | |
151 | @code |
152 | saver1->setTreeView(treeView1); |
153 | saver2->setSelectionModel(treeView2->selectionModel()); |
154 | saver3->setScrollArea(treeView3); |
155 | @endcode |
156 | |
157 | @note The KViewStateSerializer does not take ownership of any widgets set on it. |
158 | |
159 | It is recommended to restore the state on application startup and after the |
160 | model has been reset, and to save the state on application close and before |
161 | the model has been reset. |
162 | |
163 | @see QAbstractItemModel::modelAboutToBeReset QAbstractItemModel::modelReset |
164 | |
165 | @author Stephen Kelly <stephen@kdab.com> |
166 | @since 4.5 |
167 | */ |
168 | class KWIDGETSADDONS_EXPORT KViewStateSerializer : public QObject |
169 | { |
170 | Q_OBJECT |
171 | public: |
172 | /** |
173 | * Constructor |
174 | */ |
175 | explicit KViewStateSerializer(QObject *parent = nullptr); |
176 | |
177 | /** |
178 | * Destructor |
179 | */ |
180 | ~KViewStateSerializer() override; |
181 | |
182 | /** |
183 | * The view whose state is persisted. |
184 | */ |
185 | QAbstractItemView *view() const; |
186 | |
187 | /** |
188 | * Sets the view whose state is persisted. |
189 | */ |
190 | void setView(QAbstractItemView *view); |
191 | |
192 | /** |
193 | * The QItemSelectionModel whose state is persisted. |
194 | */ |
195 | QItemSelectionModel *selectionModel() const; |
196 | |
197 | /** |
198 | * Sets the QItemSelectionModel whose state is persisted. |
199 | */ |
200 | void setSelectionModel(QItemSelectionModel *selectionModel); |
201 | |
202 | /** |
203 | * Returns a QStringList describing the selection in the selectionModel. |
204 | */ |
205 | QStringList selectionKeys() const; |
206 | |
207 | /** |
208 | * Returns a QStringList representing the expanded indexes in the QTreeView. |
209 | */ |
210 | QStringList expansionKeys() const; |
211 | |
212 | /** |
213 | * Returns a QString describing the current index in the selection model. |
214 | */ |
215 | QString currentIndexKey() const; |
216 | |
217 | /** |
218 | * Returns the vertical and horizontal scroll of the QAbstractScrollArea. |
219 | */ |
220 | QPair<int, int> scrollState() const; |
221 | |
222 | /** |
223 | * Select the indexes described by @p indexStrings |
224 | */ |
225 | void restoreSelection(const QStringList &indexStrings); |
226 | |
227 | /** |
228 | * Make the index described by @p indexString the currentIndex in the selectionModel. |
229 | */ |
230 | void restoreCurrentItem(const QString &indexString); |
231 | |
232 | /** |
233 | * Expand the indexes described by @p indexStrings in the QTreeView. |
234 | */ |
235 | void restoreExpanded(const QStringList &indexStrings); |
236 | |
237 | /** |
238 | * Restores the scroll state of the QAbstractScrollArea to the @p verticalScoll |
239 | * and @p horizontalScroll |
240 | */ |
241 | void restoreScrollState(int verticalScoll, int horizontalScroll); |
242 | |
243 | protected: |
244 | /** |
245 | * Reimplement to return an index in the @p model described by the unique key @p key |
246 | */ |
247 | virtual QModelIndex indexFromConfigString(const QAbstractItemModel *model, const QString &key) const = 0; |
248 | |
249 | /** |
250 | * Reimplement to return a unique string for the @p index. |
251 | */ |
252 | virtual QString indexToConfigString(const QModelIndex &index) const = 0; |
253 | |
254 | void restoreState(); |
255 | |
256 | private: |
257 | //@cond PRIVATE |
258 | Q_DECLARE_PRIVATE(KViewStateSerializer) |
259 | std::unique_ptr<KViewStateSerializerPrivate> const d_ptr; |
260 | //@endcond |
261 | }; |
262 | |
263 | #endif |
264 | |