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.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

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