chore(StatusQ/MovableModel): storybook page showing sfpm/MovableModel and DnD working together

This commit is contained in:
Michał Cieślak 2024-02-06 11:28:39 +01:00 committed by Michał
parent 3859c5c76e
commit bb391a68ba
2 changed files with 475 additions and 5 deletions

View File

@ -1,14 +1,9 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQml 2.15
import StatusQ 0.1 import StatusQ 0.1
import Models 1.0
import Storybook 1.0
Item { Item {
id: root id: root

View File

@ -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: <b>${movableModel.detached}</b>`
}
}
}
}
// category: Models