diff --git a/src/library/autodj/dlgautodj.h b/src/library/autodj/dlgautodj.h index de5a7499d594..da3ab0506ca2 100644 --- a/src/library/autodj/dlgautodj.h +++ b/src/library/autodj/dlgautodj.h @@ -12,10 +12,10 @@ #include "library/trackcollection.h" #include "preferences/usersettings.h" #include "track/track_decl.h" +#include "widget/wtracktableview.h" class PlaylistTableModel; class WLibrary; -class WTrackTableView; class DlgAutoDJ : public QWidget, public Ui::DlgAutoDJ, public LibraryView { Q_OBJECT @@ -33,6 +33,12 @@ class DlgAutoDJ : public QWidget, public Ui::DlgAutoDJ, public LibraryView { void loadSelectedTrack() override; void loadSelectedTrackToGroup(const QString& group, bool play) override; void moveSelection(int delta) override; + void saveCurrentViewState() override { + m_pTrackTableView->saveCurrentViewState(); + }; + void restoreCurrentViewState() override { + m_pTrackTableView->restoreCurrentViewState(); + }; public slots: void shufflePlaylistButton(bool buttonChecked); diff --git a/src/library/dlganalysis.h b/src/library/dlganalysis.h index e279fcfc8a99..e9084ec796c5 100644 --- a/src/library/dlganalysis.h +++ b/src/library/dlganalysis.h @@ -3,14 +3,14 @@ #include #include -#include "preferences/usersettings.h" +#include "analyzer/analyzerprogress.h" #include "library/analysislibrarytablemodel.h" #include "library/libraryview.h" #include "library/ui_dlganalysis.h" -#include "analyzer/analyzerprogress.h" +#include "preferences/usersettings.h" +#include "widget/wanalysislibrarytableview.h" class AnalysisLibraryTableModel; -class WAnalysisLibraryTableView; class Library; class WLibrary; @@ -34,6 +34,12 @@ class DlgAnalysis : public QWidget, public Ui::DlgAnalysis, public virtual Libra inline const QString currentSearch() { return m_pAnalysisLibraryTableModel->currentSearch(); } + void saveCurrentViewState() override { + m_pAnalysisLibraryTableView->saveCurrentViewState(); + }; + void restoreCurrentViewState() override { + m_pAnalysisLibraryTableView->restoreCurrentViewState(); + }; public slots: void tableSelectionChanged(const QItemSelection& selected, diff --git a/src/library/dlghidden.h b/src/library/dlghidden.h index 9e83163997f1..1df16e20a36f 100644 --- a/src/library/dlghidden.h +++ b/src/library/dlghidden.h @@ -2,14 +2,14 @@ #include -#include "library/ui_dlghidden.h" -#include "preferences/usersettings.h" +#include "controllers/keyboard/keyboardeventfilter.h" #include "library/library.h" #include "library/libraryview.h" -#include "controllers/keyboard/keyboardeventfilter.h" +#include "library/ui_dlghidden.h" +#include "preferences/usersettings.h" +#include "widget/wtracktableview.h" class WLibrary; -class WTrackTableView; class HiddenTableModel; class DlgHidden : public QWidget, public Ui::DlgHidden, public LibraryView { @@ -25,6 +25,12 @@ class DlgHidden : public QWidget, public Ui::DlgHidden, public LibraryView { bool hasFocus() const override; void onSearch(const QString& text) override; QString currentSearch(); + void saveCurrentViewState() override { + m_pTrackTableView->saveCurrentViewState(); + }; + void restoreCurrentViewState() override { + m_pTrackTableView->restoreCurrentViewState(); + }; public slots: void clicked(); diff --git a/src/library/dlgmissing.h b/src/library/dlgmissing.h index 9fd5985eec30..48d33c50f134 100644 --- a/src/library/dlgmissing.h +++ b/src/library/dlgmissing.h @@ -2,14 +2,14 @@ #include -#include "library/ui_dlgmissing.h" -#include "preferences/usersettings.h" +#include "controllers/keyboard/keyboardeventfilter.h" #include "library/library.h" #include "library/libraryview.h" -#include "controllers/keyboard/keyboardeventfilter.h" +#include "library/ui_dlgmissing.h" +#include "preferences/usersettings.h" +#include "widget/wtracktableview.h" class WLibrary; -class WTrackTableView; class MissingTableModel; class DlgMissing : public QWidget, public Ui::DlgMissing, public LibraryView { @@ -25,6 +25,12 @@ class DlgMissing : public QWidget, public Ui::DlgMissing, public LibraryView { bool hasFocus() const override; void onSearch(const QString& text) override; QString currentSearch(); + void saveCurrentViewState() override { + m_pTrackTableView->saveCurrentViewState(); + }; + void restoreCurrentViewState() override { + m_pTrackTableView->restoreCurrentViewState(); + }; public slots: void clicked(); diff --git a/src/library/library.cpp b/src/library/library.cpp index 869d7fb3a7a2..6d88066ae696 100644 --- a/src/library/library.cpp +++ b/src/library/library.cpp @@ -355,7 +355,14 @@ void Library::bindLibraryWidget( &Library::switchToView, pLibraryWidget, &WLibrary::switchToView); - + connect(this, + &Library::saveModelState, + pTrackTableView, + &WTrackTableView::slotSaveCurrentViewState); + connect(this, + &Library::restoreModelState, + pTrackTableView, + &WTrackTableView::slotRestoreCurrentViewState); connect(pTrackTableView, &WTrackTableView::trackSelected, this, diff --git a/src/library/library.h b/src/library/library.h index f29baeec6b0e..08084d3e64cf 100644 --- a/src/library/library.h +++ b/src/library/library.h @@ -126,7 +126,7 @@ class Library: public QObject { void onSkinLoadFinished(); signals: - void showTrackModel(QAbstractItemModel* model); + void showTrackModel(QAbstractItemModel* model, bool restoreState = true); void switchToView(const QString& view); void loadTrack(TrackPointer pTrack); void loadTrackToPlayer(TrackPointer pTrack, const QString& group, bool play = false); diff --git a/src/library/libraryfeature.h b/src/library/libraryfeature.h index b59c0a23e39d..9b4a55eaf28d 100644 --- a/src/library/libraryfeature.h +++ b/src/library/libraryfeature.h @@ -121,7 +121,7 @@ class LibraryFeature : public QObject { Q_UNUSED(index); } signals: - void showTrackModel(QAbstractItemModel* model); + void showTrackModel(QAbstractItemModel* model, bool restoreState = true); void switchToView(const QString& view); void loadTrack(TrackPointer pTrack); void loadTrackToPlayer(TrackPointer pTrack, const QString& group, bool play = false); diff --git a/src/library/libraryview.h b/src/library/libraryview.h index 112c294ca2a1..5394645e0bfa 100644 --- a/src/library/libraryview.h +++ b/src/library/libraryview.h @@ -26,6 +26,8 @@ class LibraryView { virtual void slotAddToAutoDJBottom() {}; virtual void slotAddToAutoDJTop() {}; virtual void slotAddToAutoDJReplace() {}; + virtual void saveCurrentViewState(){}; + virtual void restoreCurrentViewState(){}; /// If applicable, requests that the LibraryView load the selected track to /// the specified group. Does nothing otherwise. diff --git a/src/library/recording/dlgrecording.cpp b/src/library/recording/dlgrecording.cpp index c66373aff93e..c4dd275ec9ca 100644 --- a/src/library/recording/dlgrecording.cpp +++ b/src/library/recording/dlgrecording.cpp @@ -69,6 +69,10 @@ DlgRecording::DlgRecording( &RecordingManager::durationRecorded, this, &DlgRecording::slotDurationRecorded); + connect(&m_browseModel, + &BrowseTableModel::restoreModelState, + m_pTrackTableView, + &WTrackTableView::restoreCurrentViewState); QBoxLayout* box = qobject_cast(layout()); VERIFY_OR_DEBUG_ASSERT(box) { //Assumes the form layout is a QVBox/QHBoxLayout! diff --git a/src/library/recording/dlgrecording.h b/src/library/recording/dlgrecording.h index fc8653c3ee32..4923cf1ada14 100644 --- a/src/library/recording/dlgrecording.h +++ b/src/library/recording/dlgrecording.h @@ -10,10 +10,10 @@ #include "preferences/usersettings.h" #include "recording/recordingmanager.h" #include "track/track_decl.h" +#include "widget/wtracktableview.h" class PlaylistTableModel; class WLibrary; -class WTrackTableView; class DlgRecording : public QWidget, public Ui::DlgRecording, public virtual LibraryView { Q_OBJECT @@ -33,6 +33,12 @@ class DlgRecording : public QWidget, public Ui::DlgRecording, public virtual Lib void loadSelectedTrackToGroup(const QString& group, bool play) override; void moveSelection(int delta) override; inline const QString currentSearch() { return m_proxyModel.currentSearch(); } + void saveCurrentViewState() override { + m_pTrackTableView->saveCurrentViewState(); + }; + void restoreCurrentViewState() override { + m_pTrackTableView->restoreCurrentViewState(); + }; public slots: void slotRecordingStateChanged(bool); diff --git a/src/widget/wlibrary.cpp b/src/widget/wlibrary.cpp index 9c95ec7e8e84..39b180c5f518 100644 --- a/src/widget/wlibrary.cpp +++ b/src/widget/wlibrary.cpp @@ -54,14 +54,9 @@ void WLibrary::switchToView(const QString& name) { QMutexLocker lock(&m_mutex); //qDebug() << "WLibrary::switchToView" << name; - WTrackTableView* ttView = qobject_cast( + LibraryView* oldLibraryView = dynamic_cast( currentWidget()); - if (ttView != nullptr){ - //qDebug("trying to save position"); - ttView->saveCurrentVScrollBarPos(); - } - QWidget* widget = m_viewMap.value(name, nullptr); if (widget != nullptr) { LibraryView * lview = dynamic_cast(widget); @@ -72,17 +67,13 @@ void WLibrary::switchToView(const QString& name) { return; } if (currentWidget() != widget) { + if (oldLibraryView) { + oldLibraryView->saveCurrentViewState(); + } //qDebug() << "WLibrary::setCurrentWidget" << name; setCurrentWidget(widget); lview->onShow(); - } - - WTrackTableView* ttWidgetView = qobject_cast( - widget); - - if (ttWidgetView != nullptr){ - qDebug("trying to restore position"); - ttWidgetView->restoreCurrentVScrollBarPos(); + lview->restoreCurrentViewState(); } } } diff --git a/src/widget/wlibrarytableview.cpp b/src/widget/wlibrarytableview.cpp index 366283b40aae..91de7c4962c0 100644 --- a/src/widget/wlibrarytableview.cpp +++ b/src/widget/wlibrarytableview.cpp @@ -12,14 +12,16 @@ #include "widget/wskincolor.h" #include "widget/wwidget.h" +namespace { +// number of entries in the model cache +constexpr int kModelCacheSize = 1000; +} // namespace + WLibraryTableView::WLibraryTableView(QWidget* parent, - UserSettingsPointer pConfig, - const ConfigKey& vScrollBarPosKey) + UserSettingsPointer pConfig) : QTableView(parent), m_pConfig(pConfig), - m_vScrollBarPosKey(vScrollBarPosKey) { - loadVScrollBarPosState(); - + m_modelStateCache(kModelCacheSize) { // Setup properties for table // Editing starts when clicking on an already selected item. @@ -53,37 +55,9 @@ WLibraryTableView::WLibraryTableView(QWidget* parent, } WLibraryTableView::~WLibraryTableView() { + m_modelStateCache.clear(); } -void WLibraryTableView::loadVScrollBarPosState() { - // TODO(rryan) I'm not sure I understand the value in saving the v-scrollbar - // position across restarts of Mixxx. Now that we have different views for - // each mode, the views should just maintain their scrollbar position when - // you switch views. We should discuss this. - m_noSearchVScrollBarPos = m_pConfig->getValueString(m_vScrollBarPosKey).toInt(); -} - -void WLibraryTableView::restoreNoSearchVScrollBarPos() { - // Restore the scrollbar's position (scroll to that spot) - // when the search has been cleared - //qDebug() << "restoreNoSearchVScrollBarPos()" << m_noSearchVScrollBarPos; - updateGeometries(); - verticalScrollBar()->setValue(m_noSearchVScrollBarPos); -} - -void WLibraryTableView::saveNoSearchVScrollBarPos() { - // Save the scrollbar's position so we can return here after - // a search is cleared. - //qDebug() << "saveNoSearchVScrollBarPos()" << m_noSearchVScrollBarPos; - m_noSearchVScrollBarPos = verticalScrollBar()->value(); -} - - -void WLibraryTableView::saveVScrollBarPosState() { - //Save the vertical scrollbar position. - int scrollbarPosition = verticalScrollBar()->value(); - m_pConfig->set(m_vScrollBarPosKey, ConfigValue(scrollbarPosition)); -} void WLibraryTableView::moveSelection(int delta) { QAbstractItemModel* pModel = model(); @@ -131,19 +105,63 @@ void WLibraryTableView::moveSelection(int delta) { } } -void WLibraryTableView::saveVScrollBarPos(TrackModel* key){ - m_vScrollBarPosValues[key] = verticalScrollBar()->value(); +void WLibraryTableView::saveTrackModelState( + const QAbstractItemModel* model, const QString& key) { + //qDebug() << "saveTrackModelState:" << model << key; + VERIFY_OR_DEBUG_ASSERT(model) { + return; + } + VERIFY_OR_DEBUG_ASSERT(!key.isEmpty()) { + return; + } + ModelState* state = m_modelStateCache.take(key); + if (!state) { + state = new ModelState(); + } + state->verticalScrollPosition = verticalScrollBar()->value(); + state->horizontalScrollPosition = horizontalScrollBar()->value(); + if (!selectionModel()->selectedIndexes().isEmpty()) { + state->selectionIndex = selectionModel()->selectedIndexes(); + } else { + state->selectionIndex = QModelIndexList(); + } + const QModelIndex currIndex = selectionModel()->currentIndex(); + if (currIndex.isValid()) { + state->currentIndex = currIndex; + } else { + state->currentIndex = QModelIndex(); + } + m_modelStateCache.insert(key, state, 1); } -void WLibraryTableView::restoreVScrollBarPos(TrackModel* key){ - updateGeometries(); +void WLibraryTableView::restoreTrackModelState( + const QAbstractItemModel* model, const QString& key) { + //qDebug() << "restoreTrackModelState:" << model << key; + //<< m_vModelState.keys(); + if (model == nullptr) { + return; + } - if (m_vScrollBarPosValues.contains(key)){ - verticalScrollBar()->setValue(m_vScrollBarPosValues[key]); - }else{ - m_vScrollBarPosValues[key] = 0; - verticalScrollBar()->setValue(0); + ModelState* state = m_modelStateCache.take(key); + if (!state) { + return; + } + + verticalScrollBar()->setValue(state->verticalScrollPosition); + horizontalScrollBar()->setValue(state->horizontalScrollPosition); + + auto selection = selectionModel(); + selection->clearSelection(); + if (!state->selectionIndex.isEmpty()) { + for (auto index : qAsConst(state->selectionIndex)) { + selection->select(index, QItemSelectionModel::Select); + } + } + if (state->currentIndex.isValid()) { + selection->setCurrentIndex(state->currentIndex, QItemSelectionModel::NoUpdate); } + // reinsert the state into the cache + m_modelStateCache.insert(key, state, 1); } void WLibraryTableView::setTrackTableFont(const QFont& font) { @@ -176,6 +194,24 @@ void WLibraryTableView::setSelectedClick(bool enable) { } } +void WLibraryTableView::saveCurrentViewState() { + const QAbstractItemModel* currentModel = model(); + QString key = getStateKey(); + if (!currentModel || key.isEmpty()) { + return; + } + saveTrackModelState(currentModel, key); +} + +void WLibraryTableView::restoreCurrentViewState() { + const QAbstractItemModel* currentModel = model(); + QString key = getStateKey(); + if (!currentModel || key.isEmpty()) { + return; + } + restoreTrackModelState(currentModel, key); +} + void WLibraryTableView::focusInEvent(QFocusEvent* event) { QTableView::focusInEvent(event); diff --git a/src/widget/wlibrarytableview.h b/src/widget/wlibrarytableview.h index 8db8ca726fe7..597406ea423f 100644 --- a/src/widget/wlibrarytableview.h +++ b/src/widget/wlibrarytableview.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include #include @@ -13,25 +15,32 @@ class TrackModel; class WLibraryTableView : public QTableView, public virtual LibraryView { Q_OBJECT + struct ModelState { + int horizontalScrollPosition; + int verticalScrollPosition; + QModelIndexList selectionIndex; + QModelIndex currentIndex; + }; + public: WLibraryTableView(QWidget* parent, - UserSettingsPointer pConfig, - const ConfigKey& vScrollBarPosKey); + UserSettingsPointer pConfig); ~WLibraryTableView() override; void moveSelection(int delta) override; - /** - * Saves current position of scrollbar using string key - * can be any value but should invariant for model - * @param key unique for trackmodel - */ - void saveVScrollBarPos(TrackModel* key); - /** - * Finds scrollbar value associated with model by given key and restores it - * @param key unique for trackmodel - */ - void restoreVScrollBarPos(TrackModel* key); + /// @brief saveTrackModelState function saves current positions of scrollbars, + /// current item selection and current index in a QCache using a unique + /// string key - can be any value but should invariant for model + /// @param key unique for trackmodel + void saveTrackModelState(const QAbstractItemModel* model, const QString& key); + /// @brief restoreTrackModelState function finds scrollbar positions, + /// item selection and current index values associated with model by given + /// key and restores it + /// @param key unique for trackmodel + void restoreTrackModelState(const QAbstractItemModel* model, const QString& key); + void saveCurrentViewState() override; + void restoreCurrentViewState() override; signals: void loadTrack(TrackPointer pTrack); @@ -48,20 +57,10 @@ class WLibraryTableView : public QTableView, public virtual LibraryView { protected: void focusInEvent(QFocusEvent* event) override; - void saveNoSearchVScrollBarPos(); - void restoreNoSearchVScrollBarPos(); virtual QString getStateKey() const = 0; private: - void loadVScrollBarPosState(); - void saveVScrollBarPosState(); - const UserSettingsPointer m_pConfig; - const ConfigKey m_vScrollBarPosKey; - - QMap m_vScrollBarPosValues; - - // The position of the vertical scrollbar slider, eg. before a search is - // executed - int m_noSearchVScrollBarPos; + // saves scrollposition/selection/etc + QCache m_modelStateCache; }; diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index 113c48e0f0ef..7ed80d45e3ef 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -38,9 +38,7 @@ WTrackTableView::WTrackTableView(QWidget* parent, double backgroundColorOpacity, bool sorting) : WLibraryTableView(parent, - pConfig, - ConfigKey(LIBRARY_CONFIGVALUE, - WTRACKTABLEVIEW_VSCROLLBARPOS_KEY)), + pConfig), m_pConfig(pConfig), m_pLibrary(pLibrary), m_backgroundColorOpacity(backgroundColorOpacity), @@ -139,7 +137,7 @@ void WTrackTableView::slotGuiTick50ms(double /*unused*/) { } // slot -void WTrackTableView::loadTrackModel(QAbstractItemModel* model) { +void WTrackTableView::loadTrackModel(QAbstractItemModel* model, bool restoreState) { qDebug() << "WTrackTableView::loadTrackModel()" << model; TrackModel* trackModel = dynamic_cast(model); @@ -151,21 +149,20 @@ void WTrackTableView::loadTrackModel(QAbstractItemModel* model) { return; } - // If the model has not changed - // there's no need to exchange the headers - // this will cause a small GUI freeze + // If the model has not changed there's no need to exchange the headers + // which would cause a small GUI freeze if (getTrackModel() == trackModel) { // Re-sort the table even if the track model is the same. This triggers // a select() if the table is dirty. doSortByColumn(horizontalHeader()->sortIndicatorSection(), horizontalHeader()->sortIndicatorOrder()); + + if (restoreState) { + restoreCurrentViewState(); + } return; } - // saving current vertical bar position - // using address of track model as key - saveVScrollBarPos(getTrackModel()); - setVisible(false); // Save the previous track model's header state @@ -314,9 +311,10 @@ void WTrackTableView::loadTrackModel(QAbstractItemModel* model) { setVisible(true); - restoreVScrollBarPos(trackModel); - // restoring scrollBar position using model pointer as key - // scrollbar positions with respect to different models are backed by map + // trigger restoring scrollBar position, selection etc. + if (restoreState) { + restoreCurrentViewState(); + } initTrackMenu(); } @@ -463,15 +461,9 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) { void WTrackTableView::onSearch(const QString& text) { TrackModel* trackModel = getTrackModel(); if (trackModel) { - bool searchWasEmpty = false; - if (trackModel->currentSearch().isEmpty()) { - saveNoSearchVScrollBarPos(); - searchWasEmpty = true; - } + saveCurrentViewState(); trackModel->search(text); - if (!searchWasEmpty && text.isEmpty()) { - restoreNoSearchVScrollBarPos(); - } + restoreCurrentViewState(); } } @@ -1011,14 +1003,6 @@ bool WTrackTableView::hasFocus() const { return QWidget::hasFocus(); } -void WTrackTableView::saveCurrentVScrollBarPos() { - saveVScrollBarPos(getTrackModel()); -} - -void WTrackTableView::restoreCurrentVScrollBarPos() { - restoreVScrollBarPos(getTrackModel()); -} - QString WTrackTableView::getStateKey() const { TrackModel* trackModel = getTrackModel(); if (trackModel) { diff --git a/src/widget/wtracktableview.h b/src/widget/wtracktableview.h index 263fd286b111..ea6f7321d8ca 100644 --- a/src/widget/wtracktableview.h +++ b/src/widget/wtracktableview.h @@ -18,9 +18,6 @@ class ExternalTrackCollection; class Library; class WTrackMenu; -const QString WTRACKTABLEVIEW_VSCROLLBARPOS_KEY = "VScrollBarPos"; /** ConfigValue key for QTable vertical scrollbar position */ -const QString LIBRARY_CONFIGVALUE = "[Library]"; /** ConfigValue "value" (wtf) for library stuff */ - class WTrackTableView : public WLibraryTableView { Q_OBJECT public: @@ -43,8 +40,6 @@ class WTrackTableView : public WLibraryTableView { TrackModel::SortColumnId getColumnIdFromCurrentIndex() override; QList getSelectedTrackIds() const; void setSelectedTracks(const QList& tracks); - void saveCurrentVScrollBarPos(); - void restoreCurrentVScrollBarPos(); double getBackgroundColorOpacity() const { return m_backgroundColorOpacity; @@ -56,7 +51,7 @@ class WTrackTableView : public WLibraryTableView { } public slots: - void loadTrackModel(QAbstractItemModel* model); + void loadTrackModel(QAbstractItemModel* model, bool restoreState = false); void slotMouseDoubleClicked(const QModelIndex &); void slotUnhide(); void slotPurge(); @@ -64,6 +59,12 @@ class WTrackTableView : public WLibraryTableView { void slotAddToAutoDJBottom() override; void slotAddToAutoDJTop() override; void slotAddToAutoDJReplace() override; + void slotSaveCurrentViewState() { + saveCurrentViewState(); + }; + void slotRestoreCurrentViewState() { + restoreCurrentViewState(); + }; private slots: void doSortByColumn(int headerSection, Qt::SortOrder sortOrder);