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