QmlDesigner: Implement effect maker node drag to reorder

Also small relevant tweaks

Fixes: QDS-10411
Change-Id: I332482d4726c79786edbc0a5fa1e8f6489d77f11
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Amr Elsayed <amr.elsayed@qt.io>
Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
This commit is contained in:
Mahmoud Badri
2023-09-04 14:17:01 +03:00
parent 6251730f8f
commit 9bbe78df8b
7 changed files with 147 additions and 19 deletions

View File

@@ -15,8 +15,11 @@ HelperWidgets.Section {
caption: nodeName
category: "EffectMaker"
draggable: true
fillBackground: true
showCloseButton: true
closeButtonToolTip: qsTr("Remove")
onCloseButtonClicked: {
EffectMakerBackend.effectMakerModel.removeNode(index)
}

View File

@@ -10,6 +10,11 @@ import EffectMakerBackend
Item {
id: root
property var draggedSec: null
property var secsY: []
property int moveFromIdx: 0
property int moveToIdx: 0
Column {
id: col
anchors.fill: parent
@@ -47,7 +52,6 @@ Item {
}
}
HelperWidgets.ScrollView {
id: scrollView
@@ -55,29 +59,77 @@ Item {
height: parent.height - y
clip: true
Behavior on contentY {
id: contentYBehavior
PropertyAnimation {
id: scrollViewAnim
easing.type: Easing.InOutQuad
}
}
Column {
width: scrollView.width
spacing: 1
Repeater {
id: compositionRepeater
id: repeater
width: root.width
model: EffectMakerBackend.effectMakerModel
delegate: EffectCompositionNode {
width: root.width
Behavior on y {
PropertyAnimation {
duration: 300
easing.type: Easing.InOutQuad
}
}
onStartDrag: (section) => {
root.draggedSec = section
root.moveFromIdx = index
highlightBorder = true
root.secsY = []
for (let i = 0; i < repeater.count; ++i)
root.secsY[i] = repeater.itemAt(i).y
}
onStopDrag: {
if (root.moveFromIdx === root.moveToIdx)
root.draggedSec.y = root.secsY[root.moveFromIdx]
else
EffectMakerBackend.effectMakerModel.moveNode(root.moveFromIdx, root.moveToIdx)
highlightBorder = false
root.draggedSec = null
}
}
}
}
}
} // Repeater
Timer {
running: root.draggedSec
interval: 50
repeat: true
onTriggered: {
root.moveToIdx = root.moveFromIdx
for (let i = 0; i < repeater.count; ++i) {
let currItem = repeater.itemAt(i)
if (i > root.moveFromIdx) {
if (root.draggedSec.y > currItem.y + (currItem.height - root.draggedSec.height) * .5) {
currItem.y = root.secsY[i] - root.draggedSec.height
root.moveToIdx = i
} else {
currItem.y = root.secsY[i]
}
} else if (i < root.moveFromIdx) {
if (root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) {
currItem.y = root.secsY[i] + root.draggedSec.height
root.moveToIdx = Math.min(root.moveToIdx, i)
} else {
currItem.y = root.secsY[i]
}
}
}
}
} // Timer
} // Column
} // ScrollView
}
}

View File

@@ -13,7 +13,7 @@ Rectangle {
id: root
width: 140
height: 22
height: 32
color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction
: "transparent"
@@ -38,8 +38,8 @@ Rectangle {
IconImage {
id: nodeIcon
width: 22
height: 22
width: 32
height: 32
color: StudioTheme.Values.themeTextColor
source: modelData.nodeIcon

View File

@@ -10,6 +10,8 @@ Rectangle {
id: root
signal clicked()
signal pressed()
signal released()
property alias icon: icon.text
property alias tooltip: toolTip.text
@@ -20,6 +22,7 @@ Rectangle {
property alias iconStyleColor: icon.styleColor
property alias containsMouse: mouseArea.containsMouse
property alias drag: mouseArea.drag
property bool enabled: true
property bool transparentBg: false
@@ -55,12 +58,22 @@ Rectangle {
if (root.enabled)
root.clicked()
}
onPressed: {
if (root.enabled)
root.pressed()
}
onReleased: {
if (root.enabled)
root.released()
}
}
ToolTip {
id: toolTip
visible: mouseArea.containsMouse
visible: mouseArea.containsMouse && text !== ""
delay: 1000
}
}

View File

@@ -21,6 +21,9 @@ Item {
property alias showCloseButton: closeButton.visible
property alias closeButtonToolTip: closeButton.tooltip
property alias spacing: column.spacing
property alias draggable: dragButton.visible
property alias fillBackground: sectionBackground.visible
property alias highlightBorder: sectionBorder.visible
property int leftPadding: StudioTheme.Values.sectionLeftPadding
property int rightPadding: 0
@@ -82,6 +85,8 @@ Item {
signal expand()
signal collapse()
signal closeButtonClicked()
signal startDrag(var section)
signal stopDrag()
DropArea {
id: dropArea
@@ -128,7 +133,7 @@ Item {
height: 4
source: "image://icons/down-arrow"
anchors.left: parent.left
anchors.leftMargin: 4 + (section.level * section.levelShift)
anchors.leftMargin: 4 + (section.level * section.levelShift) + (section.draggable ? 20 : 0)
anchors.verticalCenter: parent.verticalCenter
}
@@ -136,7 +141,7 @@ Item {
id: label
anchors.verticalCenter: parent.verticalCenter
color: StudioTheme.Values.themeTextColor
x: 22 + (section.level * section.levelShift)
x: arrow.x + 18
font.pixelSize: StudioTheme.Values.myFontSize
font.capitalization: Font.AllUppercase
}
@@ -174,8 +179,34 @@ Item {
onClicked: root.closeButtonClicked()
}
IconButton {
id: dragButton
icon: StudioTheme.Constants.dragmarks
buttonSize: 22
iconScale: containsMouse ? 1.2 : 1
transparentBg: true
visible: false
drag.target: section
drag.axis: Drag.YAxis
onPressed: {
section.startDrag(section)
section.z = ++section.parent.z // put the dragged section on top
}
onReleased: {
section.stopDrag()
}
}
}
Drag.active: dragButton.drag.active
Drag.source: dragButton
Rectangle {
id: topSeparator
height: 1
@@ -193,6 +224,23 @@ Item {
implicitHeight: Math.round(column.height + header.height + topSpacer.height + bottomSpacer.height)
Rectangle {
id: sectionBackground
anchors.top: header.bottom
width: section.width
height: topSpacer.height + column.height + bottomSpacer.height
color: StudioTheme.Values.themePanelBackground
visible: false
}
Rectangle {
id: sectionBorder
anchors.fill: parent
color: "transparent"
border.color: StudioTheme.Values.themeInteraction
border.width: 1
visible: false
}
Item {
id: topSpacer
height: section.addTopPadding && column.height > 0 ? section.topPadding : 0

View File

@@ -49,6 +49,17 @@ void EffectMakerModel::addNode(const QString &nodeQenPath)
endInsertRows();
}
void EffectMakerModel::moveNode(int fromIdx, int toIdx)
{
if (fromIdx == toIdx)
return;
int toIdxAdjusted = fromIdx < toIdx ? toIdx + 1 : toIdx; // otherwise beginMoveRows() crashes
beginMoveRows({}, fromIdx, fromIdx, {}, toIdxAdjusted);
m_nodes.move(fromIdx, toIdx);
endMoveRows();
}
void EffectMakerModel::removeNode(int idx)
{
beginRemoveRows({}, idx, idx);

View File

@@ -44,6 +44,7 @@ public:
void addNode(const QString &nodeQenPath);
Q_INVOKABLE void moveNode(int fromIdx, int toIdx);
Q_INVOKABLE void removeNode(int idx);
bool shadersUpToDate() const;