Meshroom/meshroom/ui/qml/GraphEditor/Edge.qml
waaake efb13f1d7d [ui] Edge: Added mouse area which checks clicks on edgeArea
The workaround on edge crashing is to use the mouse area around the edge and query if the click exists in the edge to accept pressed or released signal
2025-02-03 17:02:57 +05:30

173 lines
5.4 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Shapes 1.6
import GraphEditor 1.0
import MaterialIcons 2.2
/**
* A cubic spline representing an edge, going from point1 to point2, providing mouse interaction.
*/
Item {
id: root
property var edge
property real point1x
property real point1y
property real point2x
property real point2y
property alias thickness: path.strokeWidth
property alias color: path.strokeColor
property bool isForLoop: false
property int loopSize: 0
property int iteration: 0
// BUG: edgeArea is destroyed before path, need to test if not null to avoid warnings
readonly property bool containsMouse: (loopArea && loopArea.containsMouse) || (edgeArea && edgeArea.containsMouse)
signal pressed(var event)
signal released(var event)
x: point1x
y: point1y
width: point2x - point1x
height: point2y - point1y
property real startX: 0
property real startY: 0
property real endX: width
property real endY: height
Shape {
anchors.fill: parent
// Cause rendering artifacts when enabled (and don't support hot reload really well)
vendorExtensionsEnabled: false
opacity: 0.7
ShapePath {
id: path
startX: root.startX
startY: root.startY
fillColor: "transparent"
strokeColor: "#3E3E3E"
strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine
strokeWidth: 1
// Final visual width of this path (never below 1)
readonly property real visualWidth: Math.max(strokeWidth, 1)
dashPattern: [6 / visualWidth, 4 / visualWidth]
capStyle: ShapePath.RoundCap
PathCubic {
id: cubic
property real ctrlPtDist: 30
x: root.isForLoop ? (root.startX + root.endX) / 2 - loopArea.width / 2 : root.endX
y: root.isForLoop ? (root.startY + root.endY) / 2 : root.endY
relativeControl1X: ctrlPtDist
relativeControl1Y: 0
control2X: x - ctrlPtDist
control2Y: y
}
}
ShapePath {
id: pathSecondary
startX: (root.startX + root.endX) / 2 + loopArea.width / 2
startY: (root.startY + root.endY) / 2
fillColor: "transparent"
strokeColor: root.isForLoop ? root.color : "transparent"
strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine
strokeWidth: root.thickness
// Final visual width of this path (never below 1)
readonly property real visualWidth: Math.max(strokeWidth, 1)
dashPattern: [6 / visualWidth, 4 / visualWidth]
capStyle: ShapePath.RoundCap
PathCubic {
id: cubicSecondary
property real ctrlPtDist: 30
x: root.endX
y: root.endY
relativeControl1X: ctrlPtDist
relativeControl1Y: 0
control2X: x - ctrlPtDist
control2Y: y
}
}
}
Item {
// Place the label at the middle of the edge
x: (root.startX + root.endX) / 2
y: (root.startY + root.endY) / 2
visible: root.isForLoop
Rectangle {
anchors.centerIn: parent
property int margin: 2
width: icon.width + 2 * margin
height: icon.height + 2 * margin
radius: width
color: path.strokeColor
MaterialToolLabel {
id: icon
anchors.centerIn: parent
iconText: MaterialIcons.loop
label: (root.iteration + 1) + "/" + root.loopSize + " "
labelIconColor: palette.base
ToolTip.text: "Foreach Loop"
}
MouseArea {
id: loopArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.pressed(arguments[0])
}
}
}
EdgeMouseArea {
id: edgeArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
thickness: root.thickness + 4
curveScale: cubic.ctrlPtDist / root.width // Normalize by width
}
MouseArea {
x: Math.min(0, root.width)
y: Math.min(0, root.height)
height: Math.abs(root.height)
width: Math.abs(root.width)
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: function(event) {
let xpos = root.width < 0 ? root.height + event.x : event.x;
let ypos = root.height < 0 ? root.height + event.y: event.y;
if (edgeArea.containsPoint(Qt.point(xpos, ypos))) {
root.pressed(event);
}
else {
event.accepted = false;
}
}
onReleased: function(event) {
let xpos = root.width < 0 ? root.height + event.x : event.x;
let ypos = root.height < 0 ? root.height + event.y: event.y;
if (edgeArea.containsPoint(Qt.point(xpos, ypos))) {
root.released(event);
}
else {
event.accepted = false;
}
}
}
}