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
This commit is contained in:
Klaus Basan
2016-10-17 02:49:54 +02:00
parent 93f0e6582b
commit 630fecf8e8
8 changed files with 145 additions and 36 deletions

View File

@@ -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<ContainerType>(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 <typename ObjectType, typename ContainerType, bool UseCompare>
void CListModelBase<ObjectType, ContainerType, UseCompare>::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

View File

@@ -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<IModelFilter<ContainerType> > m_filter; //!< Used filter

View File

@@ -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;

View File

@@ -39,6 +39,6 @@ namespace BlackGui
//! Constructor
explicit CAircraftIcaoCodeView(QWidget *parent = nullptr);
};
}
}
} // ns
} // ns
#endif // guard

View File

@@ -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 <class ModelClass, class ContainerType, class ObjectType>
ObjectType CViewBase<ModelClass, ContainerType, ObjectType>::firstSelectedOrDefaultObject() const
{
if (this->hasSelection())
{
return this->selectedObjects().front();
}
if (this->rowCount() < 2)
{
return this->containerOrFilteredContainer().frontOrDefault();
}
// too many, not selected
return ObjectType();
}
template <class ModelClass, class ContainerType, class ObjectType>
int CViewBase<ModelClass, ContainerType, ObjectType>::updateSelected(const CPropertyIndexVariantMap &vm)
{
@@ -1045,6 +1059,30 @@ namespace BlackGui
this->m_model->setSorting(propertyIndex, order);
}
template <class ModelClass, class ContainerType, class ObjectType>
void CViewBase<ModelClass, ContainerType, ObjectType>::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 <class ModelClass, class ContainerType, class ObjectType>
QJsonObject CViewBase<ModelClass, ContainerType, ObjectType>::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 <class ModelClass, class ContainerType, class ObjectType>
void CViewBase<ModelClass, ContainerType, ObjectType>::reselect(const ContainerType &selectedObjects)
{
Q_UNUSED(selectedObjects);
}
template <class ModelClass, class ContainerType, class ObjectType>
CStatusMessage CViewBase<ModelClass, ContainerType, ObjectType>::modifyLoadedJsonData(ContainerType &data) const
{
@@ -1277,6 +1323,13 @@ namespace BlackGui
}
}
template <class ModelClass, class ContainerType, class ObjectType>
void CViewBase<ModelClass, ContainerType, ObjectType>::ps_selectedObjectsLoopback(const CVariant &selectedObjects)
{
const ContainerType selectedObjs = selectedObjects.value<ContainerType>();
this->reselect(selectedObjs);
}
template <class ModelClass, class ContainerType, class ObjectType>
bool CViewBase<ModelClass, ContainerType, ObjectType>::ps_filterDialogFinished(int status)
{

View File

@@ -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

View File

@@ -47,7 +47,14 @@ namespace BlackGui
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
void CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::selectDbKeys(const QList<KeyType> &keys)
void CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::selectDbKey(const KeyType &key)
{
const QSet<KeyType> set({key});
this->selectDbKeys(set);
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
void CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::selectDbKeys(const QSet<KeyType> &keys)
{
if (keys.isEmpty()) { return; }
this->clearSelection();
@@ -64,7 +71,15 @@ namespace BlackGui
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
int CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::removeDbKeys(const QList<KeyType> &keys)
QSet<KeyType> CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::selectedDbKeys() const
{
if (!this->hasSelection()) { return QSet<KeyType>(); }
const ContainerType selected(this->selectedObjects());
return selected.toDbKeySet();
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
int CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::removeDbKeys(const QSet<KeyType> &keys)
{
if (keys.isEmpty()) { return 0; }
if (this->isEmpty()) { return 0; }
@@ -148,6 +163,15 @@ namespace BlackGui
CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(menuActions);
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
void COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::reselect(const ContainerType &selectedObjects)
{
if (!selectedObjects.isEmpty())
{
this->selectDbKeys(selectedObjects.toDbKeySet());
}
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
void COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::moveSelectedItems(int order)
{

View File

@@ -27,7 +27,7 @@
#include "blackmisc/simulation/distributor.h"
#include "blackmisc/simulation/distributorlist.h"
#include <QList>
#include <QSet>
#include <QObject>
#include <QString>
#include <QtGlobal>
@@ -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<KeyType> &keys);
void selectDbKeys(const QSet<KeyType> &keys);
//! Get selected DB keys
QSet<KeyType> selectedDbKeys() const;
//! Remove keys
int removeDbKeys(const QList<KeyType> &keys);
int removeDbKeys(const QSet<KeyType> &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);