forked from qt-creator/qt-creator
ListModelEditor: Fix table action issues
* Disable delete actions when no row/column is selected * Disable move row actions when no row is selected * Provide an action for adding row below/above * Update the current item selecion after moving rows * Fix shortcut action for moving up * Select rows and columns after inserting Fixes: QDS-15011 Change-Id: If03e083ffee3446341a37b8fd8fd8978bfc20745 Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
@@ -20,8 +20,6 @@
|
|||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -46,7 +44,8 @@ ListModelEditorDialog::ListModelEditorDialog(QWidget *parent)
|
|||||||
m_tableView = new QTableView;
|
m_tableView = new QTableView;
|
||||||
mainLayout->addWidget(m_tableView);
|
mainLayout->addWidget(m_tableView);
|
||||||
|
|
||||||
m_addRowAction = toolBar->addAction(getIcon(Theme::Icon::addRowAfter), tr("Add Row"));
|
m_addRowAboveAction = toolBar->addAction(getIcon(Theme::Icon::addRowBefore), tr("Add Row Above"));
|
||||||
|
m_addRowBelowAction = toolBar->addAction(getIcon(Theme::Icon::addRowAfter), tr("Add Row Below"));
|
||||||
m_removeRowsAction = toolBar->addAction(getIcon(Theme::Icon::deleteRow), tr("Remove Rows"));
|
m_removeRowsAction = toolBar->addAction(getIcon(Theme::Icon::deleteRow), tr("Remove Rows"));
|
||||||
m_addColumnAction = toolBar->addAction(getIcon(Theme::Icon::addColumnAfter), tr("Add Column"));
|
m_addColumnAction = toolBar->addAction(getIcon(Theme::Icon::addColumnAfter), tr("Add Column"));
|
||||||
m_removeColumnsAction = toolBar->addAction(getIcon(Theme::Icon::deleteColumn),
|
m_removeColumnsAction = toolBar->addAction(getIcon(Theme::Icon::deleteColumn),
|
||||||
@@ -54,7 +53,7 @@ ListModelEditorDialog::ListModelEditorDialog(QWidget *parent)
|
|||||||
m_moveDownAction = toolBar->addAction(Icons::ARROW_DOWN.icon(), tr("Move Down (Ctrl + Down)"));
|
m_moveDownAction = toolBar->addAction(Icons::ARROW_DOWN.icon(), tr("Move Down (Ctrl + Down)"));
|
||||||
m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Down | Qt::CTRL));
|
m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Down | Qt::CTRL));
|
||||||
m_moveUpAction = toolBar->addAction(Icons::ARROW_UP.icon(), tr("Move Up (Ctrl + Up)"));
|
m_moveUpAction = toolBar->addAction(Icons::ARROW_UP.icon(), tr("Move Up (Ctrl + Up)"));
|
||||||
m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Up | Qt::CTRL));
|
m_moveUpAction->setShortcut(QKeySequence(Qt::Key_Up | Qt::CTRL));
|
||||||
}
|
}
|
||||||
|
|
||||||
ListModelEditorDialog::~ListModelEditorDialog() = default;
|
ListModelEditorDialog::~ListModelEditorDialog() = default;
|
||||||
@@ -63,7 +62,8 @@ void ListModelEditorDialog::setModel(ListModelEditorModel *model)
|
|||||||
{
|
{
|
||||||
m_model = model;
|
m_model = model;
|
||||||
|
|
||||||
connect(m_addRowAction, &QAction::triggered, m_model, &ListModelEditorModel::addRow);
|
connect(m_addRowAboveAction, &QAction::triggered, this, &ListModelEditorDialog::addRowAbove);
|
||||||
|
connect(m_addRowBelowAction, &QAction::triggered, this, &ListModelEditorDialog::addRowBelow);
|
||||||
connect(m_addColumnAction, &QAction::triggered, this, &ListModelEditorDialog::openColumnDialog);
|
connect(m_addColumnAction, &QAction::triggered, this, &ListModelEditorDialog::openColumnDialog);
|
||||||
connect(m_removeRowsAction, &QAction::triggered, this, &ListModelEditorDialog::removeRows);
|
connect(m_removeRowsAction, &QAction::triggered, this, &ListModelEditorDialog::removeRows);
|
||||||
connect(m_removeColumnsAction, &QAction::triggered, this, &ListModelEditorDialog::removeColumns);
|
connect(m_removeColumnsAction, &QAction::triggered, this, &ListModelEditorDialog::removeColumns);
|
||||||
@@ -79,16 +79,48 @@ void ListModelEditorDialog::setModel(ListModelEditorModel *model)
|
|||||||
m_tableView->verticalHeader()->setMinimumSectionSize(25);
|
m_tableView->verticalHeader()->setMinimumSectionSize(25);
|
||||||
m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
|
||||||
|
connect(model,
|
||||||
|
&ListModelEditorModel::rowsInserted,
|
||||||
|
this,
|
||||||
|
&ListModelEditorDialog::onRowsInserted,
|
||||||
|
Qt::ConnectionType::UniqueConnection);
|
||||||
|
|
||||||
|
connect(model,
|
||||||
|
&ListModelEditorModel::columnsInserted,
|
||||||
|
this,
|
||||||
|
&ListModelEditorDialog::onColumnsInserted,
|
||||||
|
Qt::ConnectionType::UniqueConnection);
|
||||||
|
|
||||||
|
connect(m_tableView->selectionModel(),
|
||||||
|
&QItemSelectionModel::selectionChanged,
|
||||||
|
this,
|
||||||
|
&ListModelEditorDialog::updateSelection,
|
||||||
|
Qt::ConnectionType::UniqueConnection);
|
||||||
|
updateSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorDialog::keyPressEvent(QKeyEvent *event)
|
void ListModelEditorDialog::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) {
|
if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) {
|
||||||
for (const QModelIndex index : m_tableView->selectionModel()->selectedIndexes())
|
const QModelIndexList selectedIndexes = m_tableView->selectionModel()->selectedIndexes();
|
||||||
|
for (const QModelIndex index : selectedIndexes)
|
||||||
m_model->setData(index, QVariant(), Qt::EditRole);
|
m_model->setData(index, QVariant(), Qt::EditRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListModelEditorDialog::addRowAbove()
|
||||||
|
{
|
||||||
|
// If there's no items in the model, or nothing is selected, The item should be prepended.
|
||||||
|
int curInteractionRow = ListModelEditorModel::currentInteractionRow(*m_tableView->selectionModel());
|
||||||
|
m_model->addRow(std::max(0, curInteractionRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListModelEditorDialog::addRowBelow()
|
||||||
|
{
|
||||||
|
m_model->addRow(ListModelEditorModel::nextInteractionRow(*m_tableView->selectionModel()));
|
||||||
|
}
|
||||||
|
|
||||||
void ListModelEditorDialog::openColumnDialog()
|
void ListModelEditorDialog::openColumnDialog()
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
@@ -126,13 +158,58 @@ void ListModelEditorDialog::changeHeader(int column)
|
|||||||
void ListModelEditorDialog::moveRowsDown()
|
void ListModelEditorDialog::moveRowsDown()
|
||||||
{
|
{
|
||||||
QItemSelection selection = m_model->moveRowsDown(m_tableView->selectionModel()->selectedRows());
|
QItemSelection selection = m_model->moveRowsDown(m_tableView->selectionModel()->selectedRows());
|
||||||
m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select);
|
m_tableView->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
m_tableView->selectionModel()->setCurrentIndex(selection.first().topLeft(),
|
||||||
|
QItemSelectionModel::Current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorDialog::moveRowsUp()
|
void ListModelEditorDialog::moveRowsUp()
|
||||||
{
|
{
|
||||||
QItemSelection selection = m_model->moveRowsUp(m_tableView->selectionModel()->selectedRows());
|
QItemSelection selection = m_model->moveRowsUp(m_tableView->selectionModel()->selectedRows());
|
||||||
m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select);
|
m_tableView->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
m_tableView->selectionModel()->setCurrentIndex(selection.first().topLeft(),
|
||||||
|
QItemSelectionModel::Current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListModelEditorDialog::updateSelection()
|
||||||
|
{
|
||||||
|
QItemSelectionModel *selection = m_tableView->selectionModel();
|
||||||
|
bool hasRowSelection = !selection->selectedRows().isEmpty();
|
||||||
|
bool hasColumnSelection = !selection->selectedColumns().isEmpty();
|
||||||
|
const int rows = m_tableView->model()->rowCount();
|
||||||
|
|
||||||
|
m_moveUpAction->setEnabled(hasRowSelection && !selection->isRowSelected(0));
|
||||||
|
m_moveDownAction->setEnabled(hasRowSelection && !selection->isRowSelected(rows - 1));
|
||||||
|
m_removeRowsAction->setEnabled(hasRowSelection);
|
||||||
|
m_removeColumnsAction->setEnabled(hasColumnSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListModelEditorDialog::onRowsInserted(const QModelIndex &parent, int first, int last)
|
||||||
|
{
|
||||||
|
QItemSelectionModel *selection = m_tableView->selectionModel();
|
||||||
|
auto model = selection->model();
|
||||||
|
const int cols = model->columnCount(parent);
|
||||||
|
auto topLeft = model->index(first, 0, parent);
|
||||||
|
auto bottomRight = model->index(last, cols - 1, parent);
|
||||||
|
QItemSelection rowsSelection{topLeft, bottomRight};
|
||||||
|
|
||||||
|
selection->select(rowsSelection, QItemSelectionModel::ClearAndSelect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListModelEditorDialog::onColumnsInserted(const QModelIndex &parent, int first, int last)
|
||||||
|
{
|
||||||
|
QItemSelectionModel *selection = m_tableView->selectionModel();
|
||||||
|
auto model = selection->model();
|
||||||
|
const int rows = model->rowCount(parent);
|
||||||
|
auto topLeft = model->index(0, first, parent);
|
||||||
|
auto bottomRight = model->index(rows - 1, last, parent);
|
||||||
|
QItemSelection columnSelection{topLeft, bottomRight};
|
||||||
|
|
||||||
|
selection->select(columnSelection, QItemSelectionModel::ClearAndSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QAbstractItemModel;
|
class QAbstractItemModel;
|
||||||
class QTableView;
|
class QTableView;
|
||||||
|
class QModelIndex;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
@@ -32,17 +33,23 @@ protected:
|
|||||||
void keyPressEvent(QKeyEvent *) override;
|
void keyPressEvent(QKeyEvent *) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addRow();
|
void addRowAbove();
|
||||||
|
void addRowBelow();
|
||||||
void openColumnDialog();
|
void openColumnDialog();
|
||||||
void removeRows();
|
void removeRows();
|
||||||
void removeColumns();
|
void removeColumns();
|
||||||
void changeHeader(int column);
|
void changeHeader(int column);
|
||||||
void moveRowsDown();
|
void moveRowsDown();
|
||||||
void moveRowsUp();
|
void moveRowsUp();
|
||||||
|
void updateSelection();
|
||||||
|
|
||||||
|
void onRowsInserted(const QModelIndex &parent, int first, int last);
|
||||||
|
void onColumnsInserted(const QModelIndex &parent, int first, int last);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ListModelEditorModel *m_model{};
|
ListModelEditorModel *m_model{};
|
||||||
QAction *m_addRowAction{};
|
QAction *m_addRowBelowAction{};
|
||||||
|
QAction *m_addRowAboveAction{};
|
||||||
QAction *m_removeRowsAction{};
|
QAction *m_removeRowsAction{};
|
||||||
QAction *m_addColumnAction{};
|
QAction *m_addColumnAction{};
|
||||||
QAction *m_removeColumnsAction{};
|
QAction *m_removeColumnsAction{};
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include <bindingproperty.h>
|
#include <bindingproperty.h>
|
||||||
#include <nodelistproperty.h>
|
#include <nodelistproperty.h>
|
||||||
#include <nodeproperty.h>
|
#include <nodeproperty.h>
|
||||||
|
#include <rewritertransaction.h>
|
||||||
#include <variantproperty.h>
|
#include <variantproperty.h>
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
@@ -213,14 +214,23 @@ void ListModelEditorModel::createItems(const QList<ModelNode> &listElementNodes)
|
|||||||
appendItems(listElementNode);
|
appendItems(listElementNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorModel::appendItems(const ModelNode &listElementNode)
|
static QList<QStandardItem *> createRow(const auto &propertyNames, const auto &listElementNode)
|
||||||
{
|
{
|
||||||
QList<QStandardItem *> row;
|
QList<QStandardItem *> row;
|
||||||
row.reserve(m_propertyNames.size());
|
row.reserve(propertyNames.size());
|
||||||
for (const PropertyName &propertyName : propertyNames())
|
for (const PropertyName &propertyName : propertyNames)
|
||||||
row.push_back(createItem(listElementNode, propertyName).release());
|
row.push_back(createItem(listElementNode, propertyName).release());
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
appendRow(row);
|
void ListModelEditorModel::insertItems(const ModelNode &listElementNode, int index)
|
||||||
|
{
|
||||||
|
insertRow(index, createRow(m_propertyNames, listElementNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListModelEditorModel::appendItems(const ModelNode &listElementNode)
|
||||||
|
{
|
||||||
|
appendRow(createRow(m_propertyNames, listElementNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorModel::setListModel(ModelNode node)
|
void ListModelEditorModel::setListModel(ModelNode node)
|
||||||
@@ -234,12 +244,18 @@ void ListModelEditorModel::setListView(ModelNode listView)
|
|||||||
setListModel(listModelNode(listView, m_createModelCallback, m_goIntoComponentCallback));
|
setListModel(listModelNode(listView, m_createModelCallback, m_goIntoComponentCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorModel::addRow()
|
void ListModelEditorModel::addRow(int rowIndex)
|
||||||
{
|
{
|
||||||
auto newElement = m_createElementCallback();
|
if (rowIndex < 0 || rowIndex > rowCount())
|
||||||
m_listModelNode.defaultNodeListProperty().reparentHere(newElement);
|
return;
|
||||||
|
|
||||||
appendItems(newElement);
|
NodeListProperty defaultNodeListProperty = m_listModelNode.defaultNodeListProperty();
|
||||||
|
defaultNodeListProperty.view()->executeInTransaction(__FUNCTION__, [&] {
|
||||||
|
auto newElement = m_createElementCallback();
|
||||||
|
defaultNodeListProperty.reparentHere(newElement);
|
||||||
|
defaultNodeListProperty.slide(defaultNodeListProperty.count() - 1, rowIndex);
|
||||||
|
insertItems(newElement, rowIndex);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorModel::addColumn(const QString &columnName)
|
void ListModelEditorModel::addColumn(const QString &columnName)
|
||||||
@@ -271,7 +287,7 @@ bool ListModelEditorModel::setValue(int row, int column, QVariant value, Qt::Ite
|
|||||||
|
|
||||||
void ListModelEditorModel::removeColumn(int column)
|
void ListModelEditorModel::removeColumn(int column)
|
||||||
{
|
{
|
||||||
QList<QStandardItem *> columnItems = QStandardItemModel::takeColumn(column);
|
QList<QStandardItem *> columnItems = Super::takeColumn(column);
|
||||||
m_propertyNames.removeAt(column);
|
m_propertyNames.removeAt(column);
|
||||||
|
|
||||||
for (QStandardItem *columnItem : columnItems) {
|
for (QStandardItem *columnItem : columnItems) {
|
||||||
@@ -302,7 +318,7 @@ void ListModelEditorModel::removeRows(const QList<QModelIndex> &indices)
|
|||||||
|
|
||||||
void ListModelEditorModel::removeRow(int row)
|
void ListModelEditorModel::removeRow(int row)
|
||||||
{
|
{
|
||||||
QList<QStandardItem *> rowItems = QStandardItemModel::takeRow(row);
|
QList<QStandardItem *> rowItems = Super::takeRow(row);
|
||||||
|
|
||||||
if (rowItems.size())
|
if (rowItems.size())
|
||||||
static_cast<ListModelItem *>(rowItems.front())->node.destroy();
|
static_cast<ListModelItem *>(rowItems.front())->node.destroy();
|
||||||
@@ -409,4 +425,27 @@ std::vector<int> ListModelEditorModel::filterRows(const QList<QModelIndex> &indi
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int interactionRow(const QItemSelectionModel &selectionModel,
|
||||||
|
QModelIndex &(QModelIndexList::*defaultRowSelector)())
|
||||||
|
{
|
||||||
|
QModelIndexList selectedRows = selectionModel.selectedRows();
|
||||||
|
auto defaultRow = std::mem_fn(defaultRowSelector);
|
||||||
|
int index = !selectedRows.isEmpty() ? defaultRow(selectedRows).row() : -1;
|
||||||
|
if (index < 0 && selectionModel.hasSelection())
|
||||||
|
index = defaultRow(selectionModel.selectedIndexes()).row();
|
||||||
|
if (index < 0 && selectionModel.currentIndex().isValid())
|
||||||
|
index = selectionModel.currentIndex().row();
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListModelEditorModel::currentInteractionRow(const QItemSelectionModel &selectionModel)
|
||||||
|
{
|
||||||
|
return interactionRow(selectionModel, &QModelIndexList::first);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListModelEditorModel::nextInteractionRow(const QItemSelectionModel &selectionModel)
|
||||||
|
{
|
||||||
|
return interactionRow(selectionModel, &QModelIndexList::last) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -12,8 +12,9 @@ namespace QmlDesigner {
|
|||||||
|
|
||||||
class ListModelEditorModel : public QStandardItemModel
|
class ListModelEditorModel : public QStandardItemModel
|
||||||
{
|
{
|
||||||
using QStandardItemModel::removeColumns;
|
using Super = QStandardItemModel;
|
||||||
using QStandardItemModel::removeRows;
|
using Super::removeColumns;
|
||||||
|
using Super::removeRows;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListModelEditorModel(std::function<ModelNode()> createModelCallback,
|
ListModelEditorModel(std::function<ModelNode()> createModelCallback,
|
||||||
@@ -28,7 +29,7 @@ public:
|
|||||||
|
|
||||||
void setListView(ModelNode listView);
|
void setListView(ModelNode listView);
|
||||||
|
|
||||||
void addRow();
|
void addRow(int rowIndex);
|
||||||
void addColumn(const QString &columnName);
|
void addColumn(const QString &columnName);
|
||||||
|
|
||||||
const QList<QmlDesigner::PropertyName> &propertyNames() const { return m_propertyNames; }
|
const QList<QmlDesigner::PropertyName> &propertyNames() const { return m_propertyNames; }
|
||||||
@@ -44,11 +45,15 @@ public:
|
|||||||
static std::vector<int> filterColumns(const QList<QModelIndex> &indices);
|
static std::vector<int> filterColumns(const QList<QModelIndex> &indices);
|
||||||
static std::vector<int> filterRows(const QList<QModelIndex> &indices);
|
static std::vector<int> filterRows(const QList<QModelIndex> &indices);
|
||||||
|
|
||||||
|
static int currentInteractionRow(const QItemSelectionModel &selectionModel);
|
||||||
|
static int nextInteractionRow(const QItemSelectionModel &selectionModel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void removeRow(int row);
|
void removeRow(int row);
|
||||||
void removeColumn(int column);
|
void removeColumn(int column);
|
||||||
void populateModel();
|
void populateModel();
|
||||||
void createItems(const QList<ModelNode> &listElementNodes);
|
void createItems(const QList<ModelNode> &listElementNodes);
|
||||||
|
void insertItems(const ModelNode &listElementNode, int index);
|
||||||
void appendItems(const ModelNode &listElementNode);
|
void appendItems(const ModelNode &listElementNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -67,7 +67,8 @@ void NodeListProperty::slide(int from, int to) const
|
|||||||
if (!isValid())
|
if (!isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (to < 0 || to > count() - 1 || from < 0 || from > count() - 1)
|
const int count = this->count();
|
||||||
|
if (to < 0 || to > count - 1 || from < 0 || from > count - 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
privateModel()->changeNodeOrder(internalNodeSharedPointer(), name(), from, to);
|
privateModel()->changeNodeOrder(internalNodeSharedPointer(), name(), from, to);
|
||||||
|
@@ -178,11 +178,31 @@ public:
|
|||||||
|
|
||||||
QModelIndex index(int row, int column) const { return model.index(row, column); }
|
QModelIndex index(int row, int column) const { return model.index(row, column); }
|
||||||
|
|
||||||
|
int rowCount() const { return model.rowCount(); }
|
||||||
|
|
||||||
QList<ModelNode> elements(const ModelNode &node) const
|
QList<ModelNode> elements(const ModelNode &node) const
|
||||||
{
|
{
|
||||||
return node.defaultNodeListProperty().toModelNodeList();
|
return node.defaultNodeListProperty().toModelNodeList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QItemSelection rowSelection(std::initializer_list<int> rows) const
|
||||||
|
{
|
||||||
|
QItemSelection selection;
|
||||||
|
for (int row : rows)
|
||||||
|
selection.select(model.index(row, 0), model.index(row, model.columnCount() - 1));
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
QItemSelection itemSelection(std::initializer_list<QPair<int, int>> items) const
|
||||||
|
{
|
||||||
|
QItemSelection selection;
|
||||||
|
for (const auto [row, column] : items) {
|
||||||
|
QModelIndex idx = index(row, column);
|
||||||
|
selection.select(idx, idx);
|
||||||
|
}
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NiceMock<ProjectStorageTriggerUpdateMock> projectStorageTriggerUpdateMock;
|
NiceMock<ProjectStorageTriggerUpdateMock> projectStorageTriggerUpdateMock;
|
||||||
NiceMock<SourcePathCacheMockWithPaths> pathCacheMock{"/path/foo.qml"};
|
NiceMock<SourcePathCacheMockWithPaths> pathCacheMock{"/path/foo.qml"};
|
||||||
@@ -200,6 +220,7 @@ protected:
|
|||||||
QmlDesigner::ListModelEditorModel model{[&] { return mockView.createModelNode("ListModel"); },
|
QmlDesigner::ListModelEditorModel model{[&] { return mockView.createModelNode("ListModel"); },
|
||||||
[&] { return mockView.createModelNode("ListElement"); },
|
[&] { return mockView.createModelNode("ListElement"); },
|
||||||
goIntoComponentMock.AsStdFunction()};
|
goIntoComponentMock.AsStdFunction()};
|
||||||
|
QItemSelectionModel selectionModel{&model};
|
||||||
ModelNode listViewNode;
|
ModelNode listViewNode;
|
||||||
ModelNode listModelNode;
|
ModelNode listModelNode;
|
||||||
ModelNode emptyListModelNode;
|
ModelNode emptyListModelNode;
|
||||||
@@ -293,7 +314,7 @@ TEST_F(ListModelEditor, add_row_added_invalid_row)
|
|||||||
{
|
{
|
||||||
model.setListModel(listModelNode);
|
model.setListModel(listModelNode);
|
||||||
|
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
|
|
||||||
ASSERT_THAT(displayValues(),
|
ASSERT_THAT(displayValues(),
|
||||||
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
@@ -302,6 +323,67 @@ TEST_F(ListModelEditor, add_row_added_invalid_row)
|
|||||||
ElementsAre(IsInvalid(), IsInvalid(), IsInvalid(), IsInvalid())));
|
ElementsAre(IsInvalid(), IsInvalid(), IsInvalid(), IsInvalid())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, add_row_to_zero_index_on_empty_model_works)
|
||||||
|
{
|
||||||
|
model.setListModel(emptyListModelNode);
|
||||||
|
model.addColumn("foo");
|
||||||
|
model.addColumn("bar");
|
||||||
|
|
||||||
|
model.addRow(0);
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(), ElementsAre(ElementsAre(IsInvalid(), IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, add_row_to_zero_index_on_non_empty_model_works)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
|
||||||
|
model.addRow(0);
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(),
|
||||||
|
ElementsAre(ElementsAre(IsInvalid(), IsInvalid(), IsInvalid(), IsInvalid()),
|
||||||
|
ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
|
ElementsAre("pic.png", "bar", 4, IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "poo", 111, IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, add_row_to_index_one_works)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
|
||||||
|
model.addRow(1);
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(),
|
||||||
|
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
|
ElementsAre(IsInvalid(), IsInvalid(), IsInvalid(), IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "bar", 4, IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "poo", 111, IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, add_row_to_negative_index_does_nothing)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
|
||||||
|
model.addRow(-1);
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(),
|
||||||
|
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
|
ElementsAre("pic.png", "bar", 4, IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "poo", 111, IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, add_row_to_int_max_index_does_nothing)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
|
||||||
|
model.addRow(std::numeric_limits<int>::max());
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(),
|
||||||
|
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
|
ElementsAre("pic.png", "bar", 4, IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "poo", 111, IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ListModelEditor, add_row_creates_new_model_node_and_reparents)
|
TEST_F(ListModelEditor, add_row_creates_new_model_node_and_reparents)
|
||||||
{
|
{
|
||||||
model.setListModel(listModelNode);
|
model.setListModel(listModelNode);
|
||||||
@@ -313,13 +395,177 @@ TEST_F(ListModelEditor, add_row_creates_new_model_node_and_reparents)
|
|||||||
_,
|
_,
|
||||||
_));
|
_));
|
||||||
|
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_returns_invalid_on_empty_selection)
|
||||||
|
{
|
||||||
|
QItemSelectionModel emptySelection;
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(emptySelection);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_returns_0_when_first_row_is_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(rowSelection({0}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_returns_1_when_second_row_is_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(rowSelection({1}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_returns_first_selected_row_when_multiple_rows_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(rowSelection({1, 2}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_returns_first_selected_item_row_when_no_row_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(itemSelection({{2, 2}, {1, 2}}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_returns_current_row_when_nothing_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.setCurrentIndex(index(2, 2), QItemSelectionModel::Current);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_prefers_selected_index_to_current_index)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(itemSelection({{2, 2}}), QItemSelectionModel::Select);
|
||||||
|
selectionModel.setCurrentIndex(index(1, 1), QItemSelectionModel::Current);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, current_interaction_row_prefers_selected_row_to_selected_index)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(itemSelection({{1, 1}}), QItemSelectionModel::Select);
|
||||||
|
selectionModel.select(rowSelection({2}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int currentRow = ListModelEditorModel::currentInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(currentRow, Eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, next_interaction_row_returns_zero_on_empty_selection)
|
||||||
|
{
|
||||||
|
QItemSelectionModel emptySelection;
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(emptySelection);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, next_interaction_row_returns_1_when_first_row_is_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(rowSelection({0}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, next_interaction_row_returns_2_when_second_row_is_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(rowSelection({1}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor,
|
||||||
|
next_interaction_row_returns_next_row_after_last_selected_row_when_multiple_rows_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(rowSelection({1, 2}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor,
|
||||||
|
next_interaction_row_returns_next_row_after_last_selected_item_row_when_no_row_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(itemSelection({{2, 2}, {1, 2}}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, next_interaction_row_returns_next_row_after_current_row_when_nothing_selected)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.setCurrentIndex(index(2, 2), QItemSelectionModel::Current);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, next_interaction_row_prefers_selected_index_to_current_index)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(itemSelection({{2, 2}}), QItemSelectionModel::Select);
|
||||||
|
selectionModel.setCurrentIndex(index(1, 1), QItemSelectionModel::Current);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, next_interaction_row_prefers_selected_row_to_selected_index)
|
||||||
|
{
|
||||||
|
model.setListModel(listModelNode);
|
||||||
|
selectionModel.select(itemSelection({{1, 1}}), QItemSelectionModel::Select);
|
||||||
|
selectionModel.select(rowSelection({2}), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
int nextRow = ListModelEditorModel::nextInteractionRow(selectionModel);
|
||||||
|
|
||||||
|
ASSERT_THAT(nextRow, Eq(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ListModelEditor, change_added_row_propery)
|
TEST_F(ListModelEditor, change_added_row_propery)
|
||||||
{
|
{
|
||||||
model.setListModel(listModelNode);
|
model.setListModel(listModelNode);
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
|
|
||||||
model.setValue(3, 2, 22);
|
model.setValue(3, 2, 22);
|
||||||
|
|
||||||
@@ -335,7 +581,7 @@ TEST_F(ListModelEditor, change_added_row_propery_calls_variant_properties_change
|
|||||||
model.setListModel(listModelNode);
|
model.setListModel(listModelNode);
|
||||||
ModelNode element4;
|
ModelNode element4;
|
||||||
ON_CALL(mockView, nodeReparented(_, _, _, _)).WillByDefault(SaveArg<0>(&element4));
|
ON_CALL(mockView, nodeReparented(_, _, _, _)).WillByDefault(SaveArg<0>(&element4));
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
|
|
||||||
EXPECT_CALL(mockView,
|
EXPECT_CALL(mockView,
|
||||||
variantPropertiesChanged(ElementsAre(IsVariantProperty(element4, "value", 22)),
|
variantPropertiesChanged(ElementsAre(IsVariantProperty(element4, "value", 22)),
|
||||||
@@ -940,7 +1186,7 @@ TEST_F(ListModelEditor, remove_last_row)
|
|||||||
{
|
{
|
||||||
model.setListModel(emptyListModelNode);
|
model.setListModel(emptyListModelNode);
|
||||||
model.addColumn("mood");
|
model.addColumn("mood");
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
|
|
||||||
model.removeRows({index(0, 0)});
|
model.removeRows({index(0, 0)});
|
||||||
|
|
||||||
@@ -951,7 +1197,7 @@ TEST_F(ListModelEditor, remove_last_empty_row)
|
|||||||
{
|
{
|
||||||
model.setListModel(emptyListModelNode);
|
model.setListModel(emptyListModelNode);
|
||||||
model.addColumn("mood");
|
model.addColumn("mood");
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
model.removeColumns({index(0, 0)});
|
model.removeColumns({index(0, 0)});
|
||||||
|
|
||||||
model.removeRows({index(0, 0)});
|
model.removeRows({index(0, 0)});
|
||||||
@@ -963,7 +1209,7 @@ TEST_F(ListModelEditor, remove_last_column)
|
|||||||
{
|
{
|
||||||
model.setListModel(emptyListModelNode);
|
model.setListModel(emptyListModelNode);
|
||||||
model.addColumn("mood");
|
model.addColumn("mood");
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
|
|
||||||
model.removeColumns({index(0, 0)});
|
model.removeColumns({index(0, 0)});
|
||||||
|
|
||||||
@@ -974,7 +1220,7 @@ TEST_F(ListModelEditor, remove_last_empty_column)
|
|||||||
{
|
{
|
||||||
model.setListModel(emptyListModelNode);
|
model.setListModel(emptyListModelNode);
|
||||||
model.addColumn("mood");
|
model.addColumn("mood");
|
||||||
model.addRow();
|
model.addRow(rowCount());
|
||||||
model.removeRows({index(0, 0)});
|
model.removeRows({index(0, 0)});
|
||||||
|
|
||||||
model.removeColumns({index(0, 0)});
|
model.removeColumns({index(0, 0)});
|
||||||
|
Reference in New Issue
Block a user