From b73ce9eccfc5f3e06fa92efb53bd8c341c13f3e7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 2 Jul 2020 19:19:52 +0200 Subject: QmlDesigner: Add row move up and move down buttons Task-number: QDS-2294 Change-Id: Ia1e64d0811f55151dfe529db4868821840a8fba9 Reviewed-by: Tim Jenssen --- .../listmodeleditor/listmodeleditordialog.cpp | 19 ++ .../listmodeleditor/listmodeleditordialog.h | 4 + .../listmodeleditor/listmodeleditormodel.cpp | 36 ++++ .../listmodeleditor/listmodeleditormodel.h | 3 + tests/unit/unittest/listmodeleditor-test.cpp | 239 +++++++++++++++++++++ 5 files changed, 301 insertions(+) diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp index 968c719af0..0cf73976a8 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp @@ -27,6 +27,7 @@ #include "listmodeleditormodel.h" #include +#include #include #include @@ -71,6 +72,10 @@ ListModelEditorDialog::ListModelEditorDialog(QWidget *parent) m_addColumnAction = toolBar->addAction(getIcon(Theme::Icon::addColumnAfter), tr("Add Column")); m_removeColumnsAction = toolBar->addAction(getIcon(Theme::Icon::deleteColumn), tr("Remove Columns")); + m_moveDownAction = toolBar->addAction(Icons::ARROW_DOWN.icon(), tr("Move down (CTRL + Down).")); + m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Down | Qt::CTRL)); + m_moveUpAction = toolBar->addAction(Icons::ARROW_UP.icon(), tr("Move up (CTRL + Up).")); + m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Up | Qt::CTRL)); } ListModelEditorDialog::~ListModelEditorDialog() = default; @@ -83,6 +88,8 @@ void ListModelEditorDialog::setModel(ListModelEditorModel *model) connect(m_addColumnAction, &QAction::triggered, this, &ListModelEditorDialog::openColumnDialog); connect(m_removeRowsAction, &QAction::triggered, this, &ListModelEditorDialog::removeRows); connect(m_removeColumnsAction, &QAction::triggered, this, &ListModelEditorDialog::removeColumns); + connect(m_moveDownAction, &QAction::triggered, this, &ListModelEditorDialog::moveRowsDown); + connect(m_moveUpAction, &QAction::triggered, this, &ListModelEditorDialog::moveRowsUp); connect(m_tableView->horizontalHeader(), &QHeaderView::sectionDoubleClicked, this, @@ -134,4 +141,16 @@ void ListModelEditorDialog::changeHeader(int column) m_model->renameColumn(column, newPropertyName); } +void ListModelEditorDialog::moveRowsDown() +{ + QItemSelection selection = m_model->moveRowsDown(m_tableView->selectionModel()->selectedRows()); + m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select); +} + +void ListModelEditorDialog::moveRowsUp() +{ + QItemSelection selection = m_model->moveRowsUp(m_tableView->selectionModel()->selectedRows()); + m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h index 519d0869fa..24e19c8ff9 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h @@ -59,6 +59,8 @@ private: void removeRows(); void removeColumns(); void changeHeader(int column); + void moveRowsDown(); + void moveRowsUp(); private: ListModelEditorModel *m_model{}; @@ -66,6 +68,8 @@ private: QAction *m_removeRowsAction{}; QAction *m_addColumnAction{}; QAction *m_removeColumnsAction{}; + QAction *m_moveUpAction{}; + QAction *m_moveDownAction{}; QTableView *m_tableView{}; }; diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp index 066af6e345..0aeabb8b89 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp @@ -319,6 +319,42 @@ void ListModelEditorModel::renameColumn(int oldColumn, const QString &newColumnN setHorizontalHeaderLabels(convertToStringList(m_propertyNames)); } +QItemSelection ListModelEditorModel::moveRowsUp(const QList &indices) +{ + std::vector rows = filterRows(indices); + + if (rows.empty() || rows.front() < 1) + return {}; + + auto nodeListProperty = m_listModelNode.defaultNodeListProperty(); + + for (int row : rows) { + insertRow(row - 1, takeRow(row)); + nodeListProperty.slide(row, row - 1); + } + + return {index(rows.front() - 1, 0), index(rows.back() - 1, columnCount() - 1)}; +} + +QItemSelection ListModelEditorModel::moveRowsDown(const QList &indices) +{ + std::vector rows = filterRows(indices); + + if (rows.empty() || rows.back() >= (rowCount() - 1)) + return {}; + + auto nodeListProperty = m_listModelNode.defaultNodeListProperty(); + + std::reverse(rows.begin(), rows.end()); + + for (int row : rows) { + insertRow(row + 1, takeRow(row)); + nodeListProperty.slide(row, row + 1); + } + + return {index(rows.front() + 1, 0), index(rows.back() + 1, columnCount() - 1)}; +} + std::vector ListModelEditorModel::filterColumns(const QList &indices) { std::vector columns; diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h index 3976b34a9d..3056d32dbb 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h @@ -27,6 +27,7 @@ #include +#include #include namespace QmlDesigner { @@ -53,6 +54,8 @@ public: void removeColumns(const QList &indices); void removeRows(const QList &indices); void renameColumn(int column, const QString &newColumnName); + QItemSelection moveRowsUp(const QList &indices); + QItemSelection moveRowsDown(const QList &indices); static std::vector filterColumns(const QList &indices); static std::vector filterRows(const QList &indices); diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp index 1dcd54c286..af4009ae9b 100644 --- a/tests/unit/unittest/listmodeleditor-test.cpp +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -175,6 +175,11 @@ public: QModelIndex index(int row, int column) const { return model.index(row, column); } + QList elements(const ModelNode &node) const + { + return node.defaultNodeListProperty().toModelNodeList(); + } + protected: std::unique_ptr designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)}; NiceMock mockView; @@ -1033,4 +1038,238 @@ TEST_F(ListModelEditor, FilterRowsEmptyInput) ASSERT_THAT(rows, IsEmpty()); } +TEST_F(ListModelEditor, CannotMoveEmptyRowsUp) +{ + model.setListModel(listModelNode); + QList indices = {index(-1, 1)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3)); +} + +TEST_F(ListModelEditor, MoveRowUp) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(1, 2), index(1, 0)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element2, element1, element3)); +} + +TEST_F(ListModelEditor, MoveRowsUp) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(2, 2), index(1, 0)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element2, element3, element1)); +} + +TEST_F(ListModelEditor, CannotMoveFirstRowsUp) +{ + model.setListModel(listModelNode); + QList indices = {index(0, 1), index(1, 2), index(0, 0)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3)); +} + +TEST_F(ListModelEditor, CannotMoveEmptyRowsUpDisplayValues) +{ + model.setListModel(listModelNode); + QList indices = {index(-1, 1)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, CannotMoveFirstRowUpDisplayValues) +{ + model.setListModel(listModelNode); + QList indices = {index(0, 1), index(1, 2), index(0, 0)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, MoveRowsUpDisplayValues) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(2, 2), index(1, 0)}; + + model.moveRowsUp(indices); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()), + ElementsAre(IsInvalid(), "foo", 1, 42))); +} + +TEST_F(ListModelEditor, NoSelectionAfterCannotMoveLastRowsDown) +{ + model.setListModel(listModelNode); + QList indices = {index(0, 1), index(1, 2), index(0, 0)}; + + auto selection = model.moveRowsUp(indices); + + ASSERT_THAT(selection.indexes(), IsEmpty()); +} + +TEST_F(ListModelEditor, NoSelectionAfterMoveEmptyRowsDown) +{ + model.setListModel(listModelNode); + QList indices = {index(-1, 1)}; + + auto selection = model.moveRowsUp(indices); + + ASSERT_THAT(selection.indexes(), IsEmpty()); +} + +TEST_F(ListModelEditor, SelectionAfterMoveRowsDown) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(2, 2), index(1, 0)}; + + auto selection = model.moveRowsUp(indices); + + ASSERT_THAT(selection.indexes(), + ElementsAre(index(0, 0), + index(0, 1), + index(0, 2), + index(0, 3), + index(1, 0), + index(1, 1), + index(1, 2), + index(1, 3))); +} + +TEST_F(ListModelEditor, CannotMoveEmptyRowsDown) +{ + model.setListModel(listModelNode); + QList indices = {index(-1, 1)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3)); +} + +TEST_F(ListModelEditor, MoveRowDown) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(1, 2), index(1, 0)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element3, element2)); +} + +TEST_F(ListModelEditor, MoveRowsDown) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(0, 2), index(1, 0)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element3, element1, element2)); +} + +TEST_F(ListModelEditor, CannotMoveLastRowsDown) +{ + model.setListModel(listModelNode); + QList indices = {index(2, 1), index(1, 2), index(2, 0)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3)); +} + +TEST_F(ListModelEditor, CannotMoveEmptyRowsDownDisplayValues) +{ + model.setListModel(listModelNode); + QList indices = {index(-1, 1)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, CannotMoveLastRowDownDisplayValues) +{ + model.setListModel(listModelNode); + QList indices = {index(2, 1), index(1, 2), index(2, 0)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, MoveRowsDownDisplayValues) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(0, 2), index(1, 0)}; + + model.moveRowsDown(indices); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre("pic.png", "poo", 111, IsInvalid()), + ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()))); +} + +TEST_F(ListModelEditor, NoSelectionAfterCannotMoveLastRowsUp) +{ + model.setListModel(listModelNode); + QList indices = {index(2, 1), index(1, 2), index(2, 0)}; + + auto selection = model.moveRowsDown(indices); + + ASSERT_THAT(selection.indexes(), IsEmpty()); +} + +TEST_F(ListModelEditor, NoSelectionAfterMoveEmptyRowsUp) +{ + model.setListModel(listModelNode); + QList indices = {index(-1, 1)}; + + auto selection = model.moveRowsDown(indices); + + ASSERT_THAT(selection.indexes(), IsEmpty()); +} + +TEST_F(ListModelEditor, SelectionAfterMoveRowsUp) +{ + model.setListModel(listModelNode); + QList indices = {index(1, 1), index(0, 2), index(1, 0)}; + + auto selection = model.moveRowsDown(indices); + + ASSERT_THAT(selection.indexes(), + ElementsAre(index(1, 0), + index(1, 1), + index(1, 2), + index(1, 3), + index(2, 0), + index(2, 1), + index(2, 2), + index(2, 3))); +} + } // namespace -- cgit v1.2.3