#1830 AppFwk : Add context menu to control state for multiple items

This commit is contained in:
Magne Sjaastad 2017-09-07 14:24:30 +02:00
parent a0a28fee36
commit 5994ef1393
5 changed files with 289 additions and 48 deletions

View File

@ -9,6 +9,7 @@ int main(int argc, char *argv[])
MainWindow window; MainWindow window;
window.setWindowTitle("Ceetron Application Framework Test Application"); window.setWindowTitle("Ceetron Application Framework Test Application");
window.resize(1000, 810);
window.show(); window.show();
return app.exec(); return app.exec();

View File

@ -40,10 +40,9 @@
#include "cafPdmObject.h" #include "cafPdmObject.h"
#include "cafPdmUiTreeSelectionQModel.h" #include "cafPdmUiTreeSelectionQModel.h"
#include "cafQTreeViewStateSerializer.h"
#include <QTreeView>
#include <QLabel> #include <QLabel>
#include <QMenu>
#include <QTreeView>
namespace caf namespace caf
@ -111,36 +110,225 @@ void PdmUiTreeSelectionEditor::configureAndUpdateUi(const QString& uiConfigName)
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSelectionStateForIndex(int index, bool isSelected) void PdmUiTreeSelectionEditor::slotSetSelectionStateForIndex(int index, bool setSelected)
{ {
unsigned int unsignedValue = static_cast<unsigned int>(index); std::vector<int> indices;
indices.push_back(index);
QVariant fieldValue = field()->uiValue(); setSelectionStateForIndices(indices, setSelected);
QList<QVariant> valuesSelectedInField = fieldValue.toList(); }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::customMenuRequested(const QPoint& pos)
{
QMenu menu;
if (!isSelected)
{ {
valuesSelectedInField.removeAll(QVariant(unsignedValue)); std::vector<int> items = selectedCheckableItems();
} if (items.size() > 0)
else
{
bool isIndexPresent = false;
for (QVariant v : valuesSelectedInField)
{ {
unsigned int indexInField = v.toUInt();
if (indexInField == unsignedValue)
{ {
isIndexPresent = true; QAction* act = new QAction("Set Selected On", this);
connect(act, SIGNAL(triggered()), SLOT(slotSetSelectedOn()));
menu.addAction(act);
}
{
QAction* act = new QAction("Set Selected Off", this);
connect(act, SIGNAL(triggered()), SLOT(slotSetSelectedOff()));
menu.addAction(act);
} }
} }
}
if (!isIndexPresent) {
std::vector<int> items = selectedHeaderItems();
if (items.size() > 0)
{ {
valuesSelectedInField.push_back(QVariant(unsignedValue)); {
QAction* act = new QAction("Set Sub Items On", this);
connect(act, SIGNAL(triggered()), SLOT(slotSetSubItemsOn()));
menu.addAction(act);
}
{
QAction* act = new QAction("Set Sub Items Off", this);
connect(act, SIGNAL(triggered()), SLOT(slotSetSubItemsOff()));
menu.addAction(act);
}
} }
} }
this->setValueToField(valuesSelectedInField); if (menu.actions().size() > 0)
{
// Qt doc: QAbstractScrollArea and its subclasses that map the context menu event to coordinates of the viewport().
QPoint globalPos = m_treeView->viewport()->mapToGlobal(pos);
menu.exec(globalPos);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSelectedOn()
{
std::vector<int> items = selectedCheckableItems();
if (items.size() > 0)
{
setSelectionStateForIndices(items, true);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSelectedOff()
{
std::vector<int> items = selectedCheckableItems();
if (items.size() > 0)
{
setSelectionStateForIndices(items, false);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSubItemsOn()
{
caf::PdmUiTreeSelectionQModel* treeSelectionQModel = dynamic_cast<caf::PdmUiTreeSelectionQModel*>(m_treeView->model());
std::vector<int> items = selectedHeaderItems();
for (auto i : items)
{
std::vector<int> children = treeSelectionQModel->allSubItemIndices(i);
setSelectionStateForIndices(children, true);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSubItemsOff()
{
caf::PdmUiTreeSelectionQModel* treeSelectionQModel = dynamic_cast<caf::PdmUiTreeSelectionQModel*>(m_treeView->model());
std::vector<int> items = selectedHeaderItems();
for (auto i : items)
{
std::vector<int> children = treeSelectionQModel->allSubItemIndices(i);
setSelectionStateForIndices(children, false);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<int> PdmUiTreeSelectionEditor::selectedCheckableItems() const
{
std::vector<int> items;
caf::PdmUiTreeSelectionQModel* treeSelectionQModel = dynamic_cast<caf::PdmUiTreeSelectionQModel*>(m_treeView->model());
if (treeSelectionQModel)
{
QModelIndexList selectedIndexes = m_treeView->selectionModel()->selectedIndexes();
for (auto mi : selectedIndexes)
{
auto optionItem = treeSelectionQModel->optionItem(mi);
if (!optionItem->isHeading())
{
items.push_back(treeSelectionQModel->optionItemIndex(mi));
}
}
}
return items;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<int> PdmUiTreeSelectionEditor::selectedHeaderItems() const
{
std::vector<int> items;
caf::PdmUiTreeSelectionQModel* treeSelectionQModel = dynamic_cast<caf::PdmUiTreeSelectionQModel*>(m_treeView->model());
if (treeSelectionQModel)
{
QModelIndexList selectedIndexes = m_treeView->selectionModel()->selectedIndexes();
for (auto mi : selectedIndexes)
{
auto optionItem = treeSelectionQModel->optionItem(mi);
if (optionItem->isHeading())
{
items.push_back(treeSelectionQModel->optionItemIndex(mi));
}
}
}
return items;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::setSelectionStateForIndices(const std::vector<int>& indices, bool setSelected)
{
std::vector<unsigned int> selectedIndices;
{
QVariant fieldValue = field()->uiValue();
QList<QVariant> fieldValueSelection = fieldValue.toList();
for (auto v : fieldValueSelection)
{
selectedIndices.push_back(v.toUInt());
}
}
for (auto index : indices)
{
unsigned int unsignedIndex = static_cast<unsigned int>(index);
if (setSelected)
{
bool isIndexPresent = false;
for (auto indexInField : selectedIndices)
{
if (indexInField == unsignedIndex)
{
isIndexPresent = true;
}
}
if (!isIndexPresent)
{
selectedIndices.push_back(unsignedIndex);
}
}
else
{
selectedIndices.erase(std::remove(selectedIndices.begin(), selectedIndices.end(), unsignedIndex), selectedIndices.end());
}
}
QList<QVariant> fieldValueSelection;
for (auto v : selectedIndices)
{
fieldValueSelection.push_back(QVariant(v));
}
this->setValueToField(fieldValueSelection);
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -151,6 +339,10 @@ QWidget* PdmUiTreeSelectionEditor::createEditorWidget(QWidget * parent)
m_treeView = new QTreeView(parent); m_treeView = new QTreeView(parent);
m_treeView->setHeaderHidden(true); m_treeView->setHeaderHidden(true);
m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_treeView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
return m_treeView; return m_treeView;
} }

View File

@ -63,7 +63,18 @@ protected:
virtual void configureAndUpdateUi(const QString& uiConfigName); virtual void configureAndUpdateUi(const QString& uiConfigName);
private slots: private slots:
void slotSetSelectionStateForIndex(int index, bool isSelected); void slotSetSelectionStateForIndex(int index, bool setSelected);
void customMenuRequested(const QPoint& pos);
void slotSetSelectedOn();
void slotSetSelectedOff();
void slotSetSubItemsOn();
void slotSetSubItemsOff();
private:
std::vector<int> selectedCheckableItems() const;
std::vector<int> selectedHeaderItems() const;
void setSelectionStateForIndices(const std::vector<int>& indices, bool setSelected);
private: private:
QPointer<QTreeView> m_treeView; QPointer<QTreeView> m_treeView;

View File

@ -83,6 +83,21 @@ int caf::PdmUiTreeSelectionQModel::optionItemCount() const
return m_options.size(); return m_options.size();
} }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const caf::PdmOptionItemInfo* caf::PdmUiTreeSelectionQModel::optionItem(const QModelIndex &index) const
{
if (index.isValid())
{
int opIndex = optionItemIndex(index);
return &(m_options[opIndex]);
}
return nullptr;
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -90,9 +105,9 @@ Qt::ItemFlags caf::PdmUiTreeSelectionQModel::flags(const QModelIndex &index) con
{ {
if (index.isValid()) if (index.isValid())
{ {
int optionItemIndex = toOptionItemIndex(index); int opIndex = optionItemIndex(index);
if (!m_options[optionItemIndex].isHeading()) if (!m_options[opIndex].isHeading())
{ {
return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable;
} }
@ -109,20 +124,20 @@ QModelIndex caf::PdmUiTreeSelectionQModel::index(int row, int column, const QMod
if (!hasIndex(row, column, parent)) if (!hasIndex(row, column, parent))
return QModelIndex(); return QModelIndex();
int optionItemIndex = -1; int opIndex = -1;
if (parent.isValid()) if (parent.isValid())
{ {
optionItemIndex = toOptionItemIndex(parent) + row + 1; opIndex = optionItemIndex(parent) + row + 1;
} }
else else
{ {
optionItemIndex = m_zeroLevelRowToOptionIndex.at(row); opIndex = m_zeroLevelRowToOptionIndex.at(row);
} }
CAF_ASSERT(optionItemIndex > -1); CAF_ASSERT(opIndex > -1);
CAF_ASSERT(optionItemIndex < m_options.size()); CAF_ASSERT(opIndex < m_options.size());
return createIndex(row, column, optionItemIndex); return createIndex(row, column, opIndex);
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -141,7 +156,7 @@ QModelIndex caf::PdmUiTreeSelectionQModel::parent(const QModelIndex &child) cons
if (!child.isValid()) if (!child.isValid())
return QModelIndex(); return QModelIndex();
return m_optionsTreeData[toOptionItemIndex(child)].parentModelIndex; return m_optionsTreeData[optionItemIndex(child)].parentModelIndex;
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -154,7 +169,7 @@ int caf::PdmUiTreeSelectionQModel::rowCount(const QModelIndex &parent /*= QModel
return m_zeroLevelRowCount; return m_zeroLevelRowCount;
} }
return m_optionsTreeData[toOptionItemIndex(parent)].childCount; return m_optionsTreeData[optionItemIndex(parent)].childCount;
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -166,13 +181,13 @@ QVariant caf::PdmUiTreeSelectionQModel::data(const QModelIndex &index, int role
{ {
CAF_ASSERT(index.internalId() < m_options.size()); CAF_ASSERT(index.internalId() < m_options.size());
int optionItemIndex = toOptionItemIndex(index); int opIndex = optionItemIndex(index);
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
{ {
return m_options[optionItemIndex].optionUiText(); return m_options[opIndex].optionUiText();
} }
else if (role == Qt::CheckStateRole && !m_options[optionItemIndex].isHeading()) else if (role == Qt::CheckStateRole && !m_options[opIndex].isHeading())
{ {
CAF_ASSERT(m_uiFieldHandle); CAF_ASSERT(m_uiFieldHandle);
@ -182,7 +197,7 @@ QVariant caf::PdmUiTreeSelectionQModel::data(const QModelIndex &index, int role
for (QVariant v : valuesSelectedInField) for (QVariant v : valuesSelectedInField)
{ {
int indexInField = v.toInt(); int indexInField = v.toInt();
if (indexInField == optionItemIndex) if (indexInField == opIndex)
{ {
return Qt::Checked; return Qt::Checked;
} }
@ -204,7 +219,7 @@ bool caf::PdmUiTreeSelectionQModel::setData(const QModelIndex &index, const QVar
{ {
bool isSelected = value.toBool(); bool isSelected = value.toBool();
emit signalSelectionStateForIndexHasChanged(toOptionItemIndex(index), isSelected); emit signalSelectionStateForIndexHasChanged(optionItemIndex(index), isSelected);
return true; return true;
} }
@ -212,6 +227,36 @@ bool caf::PdmUiTreeSelectionQModel::setData(const QModelIndex &index, const QVar
return false; return false;
} }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int caf::PdmUiTreeSelectionQModel::optionItemIndex(const QModelIndex& modelIndex) const
{
CAF_ASSERT(modelIndex.isValid());
CAF_ASSERT(modelIndex.internalId() < m_options.size());
return modelIndex.internalId();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<int> caf::PdmUiTreeSelectionQModel::allSubItemIndices(int headingIndex) const
{
std::vector<int> children;
int parentLevel = m_options[headingIndex].level();
int currentIndex = headingIndex + 1;
while (currentIndex < m_options.size() && m_options[currentIndex].level() > parentLevel)
{
children.push_back(currentIndex);
currentIndex++;
}
return children;
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -288,13 +333,3 @@ void caf::PdmUiTreeSelectionQModel::computeOptionItemTreeData()
} }
} }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int caf::PdmUiTreeSelectionQModel::toOptionItemIndex(const QModelIndex& modelIndex) const
{
CAF_ASSERT(modelIndex.isValid());
CAF_ASSERT(modelIndex.internalId() < m_options.size());
return modelIndex.internalId();
}

View File

@ -72,7 +72,10 @@ public:
void setOptions(caf::PdmUiFieldEditorHandle* field, const QList<caf::PdmOptionItemInfo>& options); void setOptions(caf::PdmUiFieldEditorHandle* field, const QList<caf::PdmOptionItemInfo>& options);
int optionItemCount() const; int optionItemCount() const;
const caf::PdmOptionItemInfo* optionItem(const QModelIndex &index) const;
int optionItemIndex(const QModelIndex& modelIndex) const;
std::vector<int> allSubItemIndices(int headingIndex) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
@ -87,7 +90,6 @@ signals:
private: private:
void computeOptionItemTreeData(); void computeOptionItemTreeData();
int toOptionItemIndex(const QModelIndex& modelIndex) const;
private: private:
QList<caf::PdmOptionItemInfo> m_options; QList<caf::PdmOptionItemInfo> m_options;