From 630fecf8e827f6abc6dd020b2e1410a05fa6c438 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Mon, 17 Oct 2016 02:49:54 +0200 Subject: [PATCH] refs #768, allow reselection of values when a view is sorted * reselect callbacks * remark: Only working in some cases as sorting is part of the model, while selection is part of the view (and sorting can take place without the view knowing the model is sorted) * allow to sort by property index * renamed to m_sortColumn --- src/blackgui/models/listmodelbase.cpp | 24 +++--- src/blackgui/models/listmodelbase.h | 12 ++- .../models/statusmessagelistmodel.cpp | 4 +- src/blackgui/views/aircrafticaoview.h | 4 +- src/blackgui/views/viewbase.cpp | 77 ++++++++++++++++--- src/blackgui/views/viewbase.h | 16 +++- src/blackgui/views/viewdbobjects.cpp | 28 ++++++- src/blackgui/views/viewdbobjects.h | 16 +++- 8 files changed, 145 insertions(+), 36 deletions(-) diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index fc76308b1..43b5ef88d 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -132,9 +132,15 @@ namespace BlackGui return this->columnToPropertyIndex(index.column()); } + void CListModelBaseNonTemplate::sortByPropertyIndex(const CPropertyIndex &propertyIndex, Qt::SortOrder order) + { + const int column = this->propertyIndexToColumn(propertyIndex); + this->sort(column, order); + } + void CListModelBaseNonTemplate::setSortColumnByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex) { - this->m_sortedColumn = this->m_columns.propertyIndexToColumn(propertyIndex); + this->m_sortColumn = this->m_columns.propertyIndexToColumn(propertyIndex); } void CListModelBaseNonTemplate::setSorting(const CPropertyIndex &propertyIndex, Qt::SortOrder order) @@ -146,8 +152,8 @@ namespace BlackGui bool CListModelBaseNonTemplate::hasValidSortColumn() const { - if (!(this->m_sortedColumn >= 0 && this->m_sortedColumn < this->m_columns.size())) { return false; } - return this->m_columns.isSortable(this->m_sortedColumn); + if (!(this->m_sortColumn >= 0 && this->m_sortColumn < this->m_columns.size())) { return false; } + return this->m_columns.isSortable(this->m_sortColumn); } Qt::ItemFlags CListModelBaseNonTemplate::flags(const QModelIndex &index) const @@ -223,7 +229,7 @@ namespace BlackGui } CListModelBaseNonTemplate::CListModelBaseNonTemplate(const QString &translationContext, QObject *parent) - : QStandardItemModel(parent), m_columns(translationContext), m_sortedColumn(-1), m_sortOrder(Qt::AscendingOrder) + : QStandardItemModel(parent), m_columns(translationContext), m_sortColumn(-1), m_sortOrder(Qt::AscendingOrder) { // non unique default name, set translation context as default this->setObjectName(translationContext); @@ -339,7 +345,7 @@ namespace BlackGui const QModelIndex topLeft = index.sibling(index.row(), 0); const QModelIndex bottomRight = index.sibling(index.row(), this->columnCount() - 1); this->m_container[index.row()] = obj; - const CVariant co = CVariant::from(obj); + const CVariant co = CVariant::fromValue(obj); emit objectChanged(co, propertyIndex); emit this->dataChanged(topLeft, bottomRight); this->updateFilteredContainer(); @@ -417,7 +423,7 @@ namespace BlackGui worker->thenWithResult(this, [this](const ContainerType & sortedContainer) { if (this->m_modelDestroyed) { return; } - this->ps_updateContainer(CVariant::from(sortedContainer), false); + this->ps_updateContainer(CVariant::fromValue(sortedContainer), false); }); worker->then(this, &CListModelBase::asyncUpdateFinished); return worker; @@ -660,11 +666,11 @@ namespace BlackGui template void CListModelBase::sort(int column, Qt::SortOrder order) { - if (column == this->m_sortedColumn && order == this->m_sortOrder) { return; } + if (column == this->m_sortColumn && order == this->m_sortOrder) { return; } // new order - this->m_sortedColumn = column; - this->m_sortOrder = order; + this->m_sortColumn = column; + this->m_sortOrder = order; if (this->m_container.size() < 2) { return; // nothing to do diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index 583a0f67c..24d8a6782 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -77,7 +77,10 @@ namespace BlackGui virtual BlackMisc::CPropertyIndex modelIndexToPropertyIndex(const QModelIndex &index) const; //! Set sort column - virtual void setSortColumn(int column) { this->m_sortedColumn = column; } + virtual void setSortColumn(int column) { this->m_sortColumn = column; } + + //! Sort by index + void sortByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder); //! Set column for sorting //! \param propertyIndex index of column to be sorted @@ -87,7 +90,7 @@ namespace BlackGui virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder); //! Get sort column property index - virtual int getSortColumn() const { return this->m_sortedColumn; } + virtual int getSortColumn() const { return this->m_sortColumn; } //! Has valid sort column? virtual bool hasValidSortColumn() const; @@ -128,6 +131,7 @@ namespace BlackGui void asyncUpdateFinished(); //! Data changed + //! \remark passing back selected objects so they can be reselected void modelDataChanged(int count, bool withFilter); //! Data changed, digest version @@ -164,7 +168,7 @@ namespace BlackGui virtual int performUpdateContainer(const BlackMisc::CVariant &variant, bool sort) = 0; CColumns m_columns; //!< columns metadata - int m_sortedColumn; //!< currently sorted column + int m_sortColumn; //!< currently sorted column bool m_modelDestroyed = false; //!< model is about to be destroyed Qt::SortOrder m_sortOrder; //!< sort order (asc/desc) Qt::DropActions m_dropActions = Qt::IgnoreAction; //!< drop actions @@ -303,7 +307,7 @@ namespace BlackGui //! Update filtered container void updateFilteredContainer(); - //! Row count changed + //! Model changed void emitModelDataChanged(); std::unique_ptr > m_filter; //!< Used filter diff --git a/src/blackgui/models/statusmessagelistmodel.cpp b/src/blackgui/models/statusmessagelistmodel.cpp index a4d9579c0..847bde440 100644 --- a/src/blackgui/models/statusmessagelistmodel.cpp +++ b/src/blackgui/models/statusmessagelistmodel.cpp @@ -49,7 +49,7 @@ namespace BlackGui this->m_columns.addColumn(CColumn::standardString("category", CStatusMessage::IndexCategoryHumanReadableOrTechnicalAsString)); this->m_columns.addColumn(CColumn::standardString("message", CStatusMessage::IndexMessage)); - this->m_sortedColumn = CStatusMessage::IndexUtcTimestamp; + this->m_sortColumn = CStatusMessage::IndexUtcTimestamp; this->m_sortOrder = Qt::DescendingOrder; } break; @@ -61,7 +61,7 @@ namespace BlackGui this->m_columns.addColumn(col); this->m_columns.addColumn(CColumn::standardString("message", CStatusMessage::IndexMessage)); - this->m_sortedColumn = CStatusMessage::IndexUtcTimestamp; + this->m_sortColumn = CStatusMessage::IndexUtcTimestamp; this->m_sortOrder = Qt::DescendingOrder; } break; diff --git a/src/blackgui/views/aircrafticaoview.h b/src/blackgui/views/aircrafticaoview.h index 1708f9dbc..5f0b411c9 100644 --- a/src/blackgui/views/aircrafticaoview.h +++ b/src/blackgui/views/aircrafticaoview.h @@ -39,6 +39,6 @@ namespace BlackGui //! Constructor explicit CAircraftIcaoCodeView(QWidget *parent = nullptr); }; - } -} + } // ns +} // ns #endif // guard diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index b6a6035c8..b23d9b266 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -97,11 +97,6 @@ namespace BlackGui CViewBaseNonTemplate::CViewBaseNonTemplate(QWidget *parent) : QTableView(parent) { - // this->viewport()->setAttribute(Qt::WA_Hover, true); - // this->viewport()->setMouseTracking(true); - // this->setStyle(new CViewBaseProxyStyle(this, this->style())); - // this->setItemDelegate(new CViewBaseItemDelegate(this)); - this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &QWidget::customContextMenuRequested, this, &CViewBaseNonTemplate::ps_customMenuRequested); connect(this, &QTableView::clicked, this, &CViewBaseNonTemplate::ps_clicked); @@ -126,7 +121,7 @@ namespace BlackGui bool CViewBaseNonTemplate::setParentDockWidgetInfoArea(CDockWidgetInfoArea *parentDockableWidget) { - bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget); + const bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget); return c; } @@ -142,7 +137,7 @@ namespace BlackGui { disconnect(this->m_filterWidget); this->menuRemoveItems(MenuFilter); - if (m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); } + if (this->m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); } m_filterWidget = nullptr; } @@ -379,8 +374,8 @@ namespace BlackGui int CViewBaseNonTemplate::getHorizontalHeaderFontHeight() const { - QFontMetrics m(this->getHorizontalHeaderFont()); - int h = m.height(); + const QFontMetrics m(this->getHorizontalHeaderFont()); + const int h = m.height(); return h; } @@ -412,7 +407,7 @@ namespace BlackGui void CViewBaseNonTemplate::init() { - int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); + const int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // faster mode this->horizontalHeader()->setStretchLastSection(true); this->verticalHeader()->setDefaultSectionSize(fh); // for height @@ -738,7 +733,10 @@ namespace BlackGui this->fullResizeToContents(); } } + + const ContainerType selected(this->selectedObjects()); const int c = this->m_model->update(container, sort); + this->reselect(selected); // resize after real update according to mode if (presizeThresholdReached) @@ -792,11 +790,11 @@ namespace BlackGui if (container.size() > ASyncRowsCountThreshold && sort) { // larger container with sorting - updateContainerAsync(container, sort, resize); + this->updateContainerAsync(container, sort, resize); } else { - updateContainer(container, sort, resize); + this->updateContainer(container, sort, resize); } } @@ -866,6 +864,22 @@ namespace BlackGui return c; } + template + ObjectType CViewBase::firstSelectedOrDefaultObject() const + { + if (this->hasSelection()) + { + return this->selectedObjects().front(); + } + if (this->rowCount() < 2) + { + return this->containerOrFilteredContainer().frontOrDefault(); + } + + // too many, not selected + return ObjectType(); + } + template int CViewBase::updateSelected(const CPropertyIndexVariantMap &vm) { @@ -1045,6 +1059,30 @@ namespace BlackGui this->m_model->setSorting(propertyIndex, order); } + template + void CViewBase::sortByPropertyIndex(const CPropertyIndex &propertyIndex, Qt::SortOrder order, bool reselect) + { + if (!reselect) + { + this->m_model->sortByPropertyIndex(propertyIndex, order); + } + else + { + // hack: we reselect the already selected objects + // as sorting takes place (sync/async) in the model, and the model does not know about the selection + // we do this deferred as the model sort can be asynchronously + const ContainerType selected(this->selectedObjects()); + this->m_model->sortByPropertyIndex(propertyIndex, order); + if (!selected.isEmpty()) + { + QTimer::singleShot(2000, [ = ]() + { + this->reselect(selected); + }); + } + } + } + template QJsonObject CViewBase::toJson() const { @@ -1137,6 +1175,8 @@ namespace BlackGui Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed"); c = connect(this->m_model, &ModelClass::changed, this, &CViewBase::onModelChanged); Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed"); + + Q_UNUSED(c); } @@ -1199,6 +1239,12 @@ namespace BlackGui this->m_dropIndicator = indicator; } + template + void CViewBase::reselect(const ContainerType &selectedObjects) + { + Q_UNUSED(selectedObjects); + } + template CStatusMessage CViewBase::modifyLoadedJsonData(ContainerType &data) const { @@ -1277,6 +1323,13 @@ namespace BlackGui } } + template + void CViewBase::ps_selectedObjectsLoopback(const CVariant &selectedObjects) + { + const ContainerType selectedObjs = selectedObjects.value(); + this->reselect(selectedObjs); + } + template bool CViewBase::ps_filterDialogFinished(int status) { diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 3ad41e619..1f78fa493 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -50,7 +50,6 @@ class QShowEvent; class QWidget; namespace BlackMisc { class CWorker; } - namespace BlackGui { class CDockWidgetInfoArea; @@ -147,6 +146,9 @@ namespace BlackGui //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::setSorting virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder) = 0; + //! Sort by index + virtual void sortByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder, bool reselect = false) = 0; + //! Allow to drag and/or drop value objects virtual void allowDragDrop(bool allowDrag, bool allowDrop) = 0; @@ -400,6 +402,9 @@ namespace BlackGui //! Helper method with template free signature serving as callback from threaded worker int ps_updateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize); + //! Helper method with template free signature to allow reselection of objects + virtual void ps_selectedObjectsLoopback(const BlackMisc::CVariant &selectedObjects) = 0; + //! Display the filter dialog void ps_displayFilterDialog(); @@ -516,6 +521,9 @@ namespace BlackGui //! Selected objects ContainerType selectedObjects() const; + //! First selected, the only one, or default + ObjectType firstSelectedOrDefaultObject() const; + //! Update selected objects int updateSelected(const BlackMisc::CVariant &variant, const BlackMisc::CPropertyIndex &index); @@ -564,6 +572,7 @@ namespace BlackGui virtual bool isDropAllowed() const override; virtual bool acceptDrop(const QMimeData *mimeData) const override; virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder) override; + virtual void sortByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder, bool reselect = false) override; //! @} //! Column count @@ -618,6 +627,10 @@ namespace BlackGui virtual void drawDropIndicator(bool indicator) override; //! @} + //! Reselect given objects + //! \remark override this function to select models again + virtual void reselect(const ContainerType &selectedObjects); + //! Modify JSON data loaded in BlackGui::Views::CViewBaseNonTemplate::ps_loadJson virtual BlackMisc::CStatusMessage modifyLoadedJsonData(ContainerType &data) const; @@ -640,6 +653,7 @@ namespace BlackGui virtual void ps_rowSelected(const QModelIndex &index) override; virtual BlackMisc::CStatusMessage ps_loadJson() override; virtual BlackMisc::CStatusMessage ps_saveJson() const override; + virtual void ps_selectedObjectsLoopback(const BlackMisc::CVariant &selectedObjects) override; //! @} }; } // namespace diff --git a/src/blackgui/views/viewdbobjects.cpp b/src/blackgui/views/viewdbobjects.cpp index eefb8bee6..b6a212721 100644 --- a/src/blackgui/views/viewdbobjects.cpp +++ b/src/blackgui/views/viewdbobjects.cpp @@ -47,7 +47,14 @@ namespace BlackGui } template - void CViewWithDbObjects::selectDbKeys(const QList &keys) + void CViewWithDbObjects::selectDbKey(const KeyType &key) + { + const QSet set({key}); + this->selectDbKeys(set); + } + + template + void CViewWithDbObjects::selectDbKeys(const QSet &keys) { if (keys.isEmpty()) { return; } this->clearSelection(); @@ -64,7 +71,15 @@ namespace BlackGui } template - int CViewWithDbObjects::removeDbKeys(const QList &keys) + QSet CViewWithDbObjects::selectedDbKeys() const + { + if (!this->hasSelection()) { return QSet(); } + const ContainerType selected(this->selectedObjects()); + return selected.toDbKeySet(); + } + + template + int CViewWithDbObjects::removeDbKeys(const QSet &keys) { if (keys.isEmpty()) { return 0; } if (this->isEmpty()) { return 0; } @@ -148,6 +163,15 @@ namespace BlackGui CViewWithDbObjects::customMenu(menuActions); } + template + void COrderableViewWithDbObjects::reselect(const ContainerType &selectedObjects) + { + if (!selectedObjects.isEmpty()) + { + this->selectDbKeys(selectedObjects.toDbKeySet()); + } + } + template void COrderableViewWithDbObjects::moveSelectedItems(int order) { diff --git a/src/blackgui/views/viewdbobjects.h b/src/blackgui/views/viewdbobjects.h index e2cb687ea..903e8b7cb 100644 --- a/src/blackgui/views/viewdbobjects.h +++ b/src/blackgui/views/viewdbobjects.h @@ -27,7 +27,7 @@ #include "blackmisc/simulation/distributor.h" #include "blackmisc/simulation/distributorlist.h" -#include +#include #include #include #include @@ -40,7 +40,6 @@ class QWidget; namespace BlackGui { namespace Menus { class CMenuActions; } - namespace Views { //! Base class for views with DB objects @@ -54,11 +53,17 @@ namespace BlackGui //! Get oldets object ObjectType oldestObject() const; + //! Select given DB key + void selectDbKey(const KeyType &key); + //! Select given DB keys - void selectDbKeys(const QList &keys); + void selectDbKeys(const QSet &keys); + + //! Get selected DB keys + QSet selectedDbKeys() const; //! Remove keys - int removeDbKeys(const QList &keys); + int removeDbKeys(const QSet &keys); //! Update or insert data (based on DB key) int replaceOrAddObjectsByKey(const ContainerType &container); @@ -82,6 +87,9 @@ namespace BlackGui //! \copydoc BlackGui::Views::CViewBaseNonTemplate::customMenu virtual void customMenu(BlackGui::Menus::CMenuActions &menuActions) override; + //! Reselect by DB keys + virtual void reselect(const ContainerType &selectedObjects) override; + //! Move selected items void moveSelectedItems(int order);