482 lines
14 KiB
QML
482 lines
14 KiB
QML
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: "desynchronize"
|
||
|
||
onClicked: movableModel.desyncOrder()
|
||
}
|
||
|
||
Button {
|
||
text: "synchronize"
|
||
|
||
onClicked: movableModel.syncOrder()
|
||
}
|
||
|
||
Label {
|
||
text: `Synchronized: <b>${movableModel.synced}</b>`
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// category: Models
|