From bb391a68ba544b789655f909d59136da5c6c3f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Cie=C5=9Blak?= Date: Tue, 6 Feb 2024 11:28:39 +0100 Subject: [PATCH] chore(StatusQ/MovableModel): storybook page showing sfpm/MovableModel and DnD working together --- storybook/pages/MovableModelPage.qml | 5 - storybook/pages/MovableModelWithSfpmPage.qml | 475 +++++++++++++++++++ 2 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 storybook/pages/MovableModelWithSfpmPage.qml diff --git a/storybook/pages/MovableModelPage.qml b/storybook/pages/MovableModelPage.qml index d8a83d9a3d..79583e377e 100644 --- a/storybook/pages/MovableModelPage.qml +++ b/storybook/pages/MovableModelPage.qml @@ -1,14 +1,9 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 -import QtQml 2.15 import StatusQ 0.1 -import Models 1.0 -import Storybook 1.0 - - Item { id: root diff --git a/storybook/pages/MovableModelWithSfpmPage.qml b/storybook/pages/MovableModelWithSfpmPage.qml new file mode 100644 index 0000000000..3af3663ed3 --- /dev/null +++ b/storybook/pages/MovableModelWithSfpmPage.qml @@ -0,0 +1,475 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ 0.1 + +import SortFilterProxyModel 0.2 + +Item { + id: root + + ListModel { + id: simpleSourceModel + + ListElement { + name: "entry 0" + position: 0 + } + ListElement { + name: "entry 1" + position: 1 + } + ListElement { + name: "entry 2" + position: 2 + } + ListElement { + name: "entry 3" + position: 3 + } + ListElement { + name: "entry 4" + position: 7 + } + ListElement { + name: "entry 5" + position: 6 + } + ListElement { + name: "entry 6" + position: 5 + } + ListElement { + name: "entry 7" + position: 4 + } + } + + SortFilterProxyModel { + id: sorted + + sorters: RoleSorter { + roleName: sortByPositionRadioButton.checked ? "position" : "name" + sortOrder: descendingCheckBox.checked ? Qt.DescendingOrder + : Qt.AscendingOrder + } + + sourceModel: simpleSourceModel + } + + MovableModel { + id: movableModel + + sourceModel: sorted + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 10 + spacing: 5 + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout { + id: sourceColumn + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.margins: 20 + spacing: 20 + + width: parent.width / 3 + + Label { + text: "SOURCE MODEL" + + font.bold: true + font.pixelSize: 17 + } + + ListView { + id: sourceListView + + spacing: 5 + clip: true + + Layout.fillWidth: true + Layout.fillHeight: true + + model: simpleSourceModel + + ScrollBar.vertical: ScrollBar {} + + delegate: Item { + id: sourceDelegateRoot + + anchors { + left: parent ? parent.left : undefined + right: parent ? parent.right : undefined + } + + height: sourceContent.implicitHeight + + RowLayout { + id: sourceContent + + Rectangle { + color: "lightgray" + Layout.fillHeight: true + Layout.preferredWidth: 40 + + Label { + anchors.centerIn: parent + text: "↕️" + } + + MouseArea { + id: sourceDragArea + + property bool held: false + readonly property int idx: model.index + + anchors.fill: parent + + drag.target: held ? sourceContent : undefined + drag.axis: Drag.YAxis + + onPressed: held = true + onReleased: held = false + } + } + + width: sourceDelegateRoot.width + + Drag.active: sourceDragArea.held + Drag.source: sourceDragArea + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + + states: State { + when: sourceDragArea.held + + ParentChange { + target: sourceContent + parent: root + } + + AnchorChanges { + target: sourceContent + anchors { + horizontalCenter: undefined + verticalCenter: undefined + } + } + } + + Label { + Layout.fillWidth: true + + font.bold: true + text: model.name + ", position: " + model.position + } + + RoundButton { + text: "❌" + + onClicked: simpleSourceModel.remove(model.index) + } + + RoundButton { + text: "✎" + + onClicked: simpleSourceModel.setProperty( + index, "name", simpleSourceModel.get(index).name + "_") + } + } + + DropArea { + anchors { fill: parent; margins: 10 } + + onEntered: { + const from = drag.source.idx + const to = sourceDragArea.idx + + if (from === to) + return + + simpleSourceModel.move(from, to, 1) + } + } + } + } + } + + ColumnLayout { + id: sfpmColumn + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: sourceColumn.right + anchors.margins: 20 + + spacing: 20 + + width: parent.width / 3 + + Label { + text: "SFPM MODEL" + + font.bold: true + font.pixelSize: 17 + } + + ListView { + id: sfpmListView + + spacing: 5 + clip: true + + Layout.fillWidth: true + Layout.fillHeight: true + + model: sorted + + ScrollBar.vertical: ScrollBar {} + + delegate: RowLayout { + width: ListView.view.width + + Label { + Layout.fillWidth: true + + font.bold: true + text: model.name + ", position: " + model.position + } + + // to keep the same delegate height as in other list views + RoundButton { + enabled: false + opacity: 0 + } + } + } + } + + ColumnLayout { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: sfpmColumn.right + anchors.right: parent.right + anchors.margins: 20 + + spacing: 20 + + Label { + text: "MOVABLE MODEL" + font.bold: true + font.pixelSize: 17 + } + + ListView { + id: transformedListView + + Layout.fillWidth: true + Layout.fillHeight: true + + spacing: 5 + clip: true + + model: movableModel + + ScrollBar.vertical: ScrollBar {} + + delegate: Item { + id: delegateRoot + + anchors { + left: parent ? parent.left : undefined + right: parent ? parent.right : undefined + } + + height: content.implicitHeight + + RowLayout { + id: content + + Rectangle { + + color: "lightgray" + Layout.fillHeight: true + Layout.preferredWidth: 40 + + Label { + anchors.centerIn: parent + text: "↕️" + } + + MouseArea { + id: dragArea + + property bool held: false + readonly property int idx: model.index + + anchors.fill: parent + + drag.target: held ? content : undefined + drag.axis: Drag.YAxis + + onPressed: held = true + onReleased: held = false + } + } + + width: delegateRoot.width + + Drag.active: dragArea.held + Drag.source: dragArea + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + + states: State { + when: dragArea.held + + ParentChange { + target: content + parent: root + } + + AnchorChanges { + target: content + anchors { + horizontalCenter: undefined + verticalCenter: undefined + } + } + } + + Label { + Layout.fillWidth: true + + font.bold: true + text: model.name + ", position: " + model.position + } + + RoundButton { + text: "⬆️" + enabled: index > 0 + + onClicked: movableModel.move(index, 0) + } + RoundButton { + text: "⬇️" + enabled: index < transformedListView.count - 1 + + onClicked: movableModel.move( + index, transformedListView.count - 1) + } + } + + DropArea { + anchors { fill: parent; margins: 10 } + + onEntered: { + const from = drag.source.idx + const to = dragArea.idx + + if (from === to) + return + + movableModel.move(from, to) + } + } + } + } + } + + Rectangle { + width: 1 + color: "gray" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: sourceColumn.right + anchors.leftMargin: 10 + } + + Rectangle { + width: 1 + color: "gray" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: sfpmColumn.right + anchors.leftMargin: 10 + } + } + + Button { + Layout.alignment: Qt.AlignHCenter + + text: "SAVE ORDER" + font.pixelSize: 30 + + onClicked: { + const count = simpleSourceModel.count + const newOrder = movableModel.order() + const newOrderInverted = [] + const sourceIndexes = [] + + for (let i = 0; i < count; i++) + newOrderInverted[newOrder[i]] = i + + for (let j = 0; j < count; j++) + sourceIndexes.push(sorted.mapToSource(j)) + + for (let k = 0; k < count; k++) + simpleSourceModel.setProperty(sourceIndexes[k], "position", + newOrderInverted[k]) + } + } + + RowLayout { + Layout.fillHeight: false + + RadioButton { + text: "Sort by name" + } + + RadioButton { + id: sortByPositionRadioButton + + text: "Sort by position" + checked: true + } + + CheckBox { + id: descendingCheckBox + text: "descending" + } + } + + RowLayout { + Layout.fillHeight: false + + Button { + text: "append to source model" + + onClicked: simpleSourceModel.append({ name: "X" }) + } + + Button { + text: "detach order explicitely" + + onClicked: movableModel.detach() + } + + Label { + text: `Detached: ${movableModel.detached}` + } + } + } +} + +// category: Models