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

source code of kwidgetsaddons/src/kviewstateserializer.h