File indexing completed on 2024-04-28 03:59:17

0001 /*
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0003     SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KVIEWSTATESERIALIZER_H
0009 #define KVIEWSTATESERIALIZER_H
0010 
0011 #include <QObject>
0012 #include <QPair>
0013 #include <QStringList>
0014 #include <memory>
0015 
0016 #include "kwidgetsaddons_export.h"
0017 
0018 class QAbstractItemView;
0019 class QItemSelectionModel;
0020 class QAbstractItemModel;
0021 class QAbstractScrollArea;
0022 class QModelIndex;
0023 
0024 class KViewStateSerializerPrivate;
0025 
0026 /**
0027   @class KViewStateSerializer kviewstateserializer.h KViewStateSerializer
0028 
0029   @brief Object for saving and restoring state in QTreeViews and QItemSelectionModels
0030 
0031   Implement the indexFromConfigString and indexToConfigString methods to handle
0032   the model in the view whose state is being saved. These implementations can be
0033   quite trivial:
0034 
0035   @code
0036     QModelIndex DynamicTreeStateSaver::indexFromConfigString(const QAbstractItemModel* model,
0037                                                              const QString& key) const
0038     {
0039         QModelIndexList list = model->match(model->index(0, 0),
0040                                             DynamicTreeModel::DynamicTreeModelId,
0041                                             key.toInt(), 1, Qt::MatchRecursive);
0042         if (list.isEmpty()) {
0043             return QModelIndex();
0044         }
0045         return list.first();
0046     }
0047 
0048     QString DynamicTreeStateSaver::indexToConfigString(const QModelIndex& index) const
0049     {
0050         return index.data(DynamicTreeModel::DynamicTreeModelId).toString();
0051     }
0052   @endcode
0053 
0054   It is possible to restore the state of a QTreeView (that is, the expanded state and
0055   selected state of all indexes as well as the horizontal and vertical scroll state) by
0056   using setTreeView.
0057 
0058   If there is no tree view state to restore (for example if using QML), the selection
0059   state of a QItemSelectionModel can be saved or restored instead.
0060 
0061   The state of any QAbstractScrollArea can also be saved and restored.
0062 
0063   A KViewStateSerializer should be created on the stack when saving and on the heap
0064   when restoring. The model may be populated dynamically between several event loops,
0065   so it may not be immediate for the indexes that should be selected to be in the model.
0066   The saver should *not* be persisted as a member. The saver will destroy itself when it
0067   has completed the restoration specified in the config group, or a small amount of time
0068   has elapsed.
0069 
0070   @code
0071     MyWidget::MyWidget(Qobject *parent)
0072       : QWidget(parent)
0073     {
0074         ...
0075 
0076         m_view = new QTreeView(splitter);
0077         m_view->setModel(model);
0078 
0079         connect(model, &QAbstractItemModel::modelAboutToBeReset, this, [this]() { saveState(); });
0080         connect(model, &QAbstractItemModel::modelReset, [this]() { restoreState(); });
0081         connect(qApp, &QApplication::aboutToQuit, this, [this]() { saveState(); });
0082 
0083         restoreState();
0084     }
0085 
0086     void StateSaverWidget::saveState()
0087     {
0088         ConcreteStateSaver saver;
0089         saver.setTreeView(m_view);
0090 
0091         KConfigGroup cfg(KSharedConfig::openConfig(), "ExampleViewState");
0092         saver.saveState(cfg);
0093         cfg.sync();
0094     }
0095 
0096     void StateSaverWidget::restoreState()
0097     {
0098         // Will delete itself.
0099         ConcreteTreeStateSaver *saver = new ConcreteStateSaver();
0100         saver->setTreeView(m_view);
0101         KConfigGroup cfg(KSharedConfig::openConfig(), "ExampleViewState");
0102         saver->restoreState(cfg);
0103     }
0104   @endcode
0105 
0106   After creating a saver, the state can be saved using a KConfigGroup.
0107 
0108   It is also possible to save and restore state directly by using the restoreSelection,
0109   restoreExpanded etc methods. Note that the implementation of these methods should return
0110   strings that the indexFromConfigString implementation can handle.
0111 
0112   @code
0113     class DynamicTreeStateSaver : public KViewStateSerializer
0114     {
0115         Q_OBJECT
0116     public:
0117       // ...
0118 
0119         void selectItems(const QList<qint64> &items)
0120         {
0121             QStringList itemStrings;
0122             for (qint64 item : items) {
0123                 itemStrings << QString::number(item);
0124             }
0125             restoreSelection(itemStrings);
0126         }
0127 
0128         void expandItems(const QList<qint64> &items)
0129         {
0130             QStringList itemStrings;
0131             for (qint64 item : items) {
0132                 itemStrings << QString::number(item);
0133             }
0134             restoreSelection(itemStrings);
0135         }
0136     };
0137   @endcode
0138 
0139   Note that a single instance of this class should be used with only one widget.
0140   That is don't do this:
0141 
0142   @code
0143     saver->setTreeView(treeView1);
0144     saver->setSelectionModel(treeView2->selectionModel());
0145     saver->setScrollArea(treeView3);
0146   @endcode
0147 
0148   To save the state of 3 different widgets, use three savers, even if they operate
0149   on the same root model.
0150 
0151   @code
0152     saver1->setTreeView(treeView1);
0153     saver2->setSelectionModel(treeView2->selectionModel());
0154     saver3->setScrollArea(treeView3);
0155   @endcode
0156 
0157   @note The KViewStateSerializer does not take ownership of any widgets set on it.
0158 
0159   It is recommended to restore the state on application startup and after the
0160   model has been reset, and to save the state on application close and before
0161   the model has been reset.
0162 
0163   @see QAbstractItemModel::modelAboutToBeReset QAbstractItemModel::modelReset
0164 
0165   @author Stephen Kelly <stephen@kdab.com>
0166   @since 4.5
0167 */
0168 class KWIDGETSADDONS_EXPORT KViewStateSerializer : public QObject
0169 {
0170     Q_OBJECT
0171 public:
0172     /**
0173      * Constructor
0174      */
0175     explicit KViewStateSerializer(QObject *parent = nullptr);
0176 
0177     /**
0178      * Destructor
0179      */
0180     ~KViewStateSerializer() override;
0181 
0182     /**
0183      * The view whose state is persisted.
0184      */
0185     QAbstractItemView *view() const;
0186 
0187     /**
0188      * Sets the view whose state is persisted.
0189      */
0190     void setView(QAbstractItemView *view);
0191 
0192     /**
0193      * The QItemSelectionModel whose state is persisted.
0194      */
0195     QItemSelectionModel *selectionModel() const;
0196 
0197     /**
0198      * Sets the QItemSelectionModel whose state is persisted.
0199      */
0200     void setSelectionModel(QItemSelectionModel *selectionModel);
0201 
0202     /**
0203      * Returns a QStringList describing the selection in the selectionModel.
0204      */
0205     QStringList selectionKeys() const;
0206 
0207     /**
0208      * Returns a QStringList representing the expanded indexes in the QTreeView.
0209      */
0210     QStringList expansionKeys() const;
0211 
0212     /**
0213      * Returns a QString describing the current index in the selection model.
0214      */
0215     QString currentIndexKey() const;
0216 
0217     /**
0218      * Returns the vertical and horizontal scroll of the QAbstractScrollArea.
0219      */
0220     QPair<int, int> scrollState() const;
0221 
0222     /**
0223      * Select the indexes described by @p indexStrings
0224      */
0225     void restoreSelection(const QStringList &indexStrings);
0226 
0227     /**
0228      * Make the index described by @p indexString the currentIndex in the selectionModel.
0229      */
0230     void restoreCurrentItem(const QString &indexString);
0231 
0232     /**
0233      * Expand the indexes described by @p indexStrings in the QTreeView.
0234      */
0235     void restoreExpanded(const QStringList &indexStrings);
0236 
0237     /**
0238      * Restores the scroll state of the QAbstractScrollArea to the @p verticalScoll
0239      * and @p horizontalScroll
0240      */
0241     void restoreScrollState(int verticalScoll, int horizontalScroll);
0242 
0243 protected:
0244     /**
0245      * Reimplement to return an index in the @p model described by the unique key @p key
0246      */
0247     virtual QModelIndex indexFromConfigString(const QAbstractItemModel *model, const QString &key) const = 0;
0248 
0249     /**
0250      * Reimplement to return a unique string for the @p index.
0251      */
0252     virtual QString indexToConfigString(const QModelIndex &index) const = 0;
0253 
0254     void restoreState();
0255 
0256 private:
0257     //@cond PRIVATE
0258     Q_DECLARE_PRIVATE(KViewStateSerializer)
0259     std::unique_ptr<KViewStateSerializerPrivate> const d_ptr;
0260     //@endcond
0261 };
0262 
0263 #endif