Skip to content

Commit 02087f5

Browse files
committed
[ui] Utils: add SelectionBox and DelegateSelectionBox
- SelectionBox: generic Selection box component. - DelegateSelectionBox: specialized SelectionBox to select model delegates from an instantiator (Repeater, ListView). Also Introduce a Geom2D helper class to provide missing features for intersection testing in QML.
1 parent 42c2c2b commit 02087f5

File tree

5 files changed

+106
-1
lines changed

5 files changed

+106
-1
lines changed

meshroom/ui/components/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11

22
def registerTypes():
3-
from PySide6.QtQml import qmlRegisterType
3+
from PySide6.QtQml import qmlRegisterType, qmlRegisterSingletonType
44
from meshroom.ui.components.clipboard import ClipboardHelper
55
from meshroom.ui.components.edge import EdgeMouseArea
66
from meshroom.ui.components.filepath import FilepathHelper
77
from meshroom.ui.components.scene3D import Scene3DHelper, TrackballController, Transformations3DHelper
88
from meshroom.ui.components.csvData import CsvData
9+
from meshroom.ui.components.geom2D import Geom2D
910

1011
qmlRegisterType(EdgeMouseArea, "GraphEditor", 1, 0, "EdgeMouseArea")
1112
qmlRegisterType(ClipboardHelper, "Meshroom.Helpers", 1, 0, "ClipboardHelper") # TODO: uncreatable
@@ -14,3 +15,5 @@ def registerTypes():
1415
qmlRegisterType(Transformations3DHelper, "Meshroom.Helpers", 1, 0, "Transformations3DHelper") # TODO: uncreatable
1516
qmlRegisterType(TrackballController, "Meshroom.Helpers", 1, 0, "TrackballController")
1617
qmlRegisterType(CsvData, "DataObjects", 1, 0, "CsvData")
18+
19+
qmlRegisterSingletonType(Geom2D, "Meshroom.Helpers", 1, 0, "Geom2D")

meshroom/ui/components/geom2D.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from PySide6.QtCore import QObject, Slot, QRectF
2+
3+
4+
class Geom2D(QObject):
5+
@Slot(QRectF, QRectF, result=bool)
6+
def rectRectIntersect(self, rect1: QRectF, rect2: QRectF) -> bool:
7+
"""Check if two rectangles intersect."""
8+
return rect1.intersects(rect2)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import QtQuick
2+
import Meshroom.Helpers
3+
4+
/*
5+
A SelectionBox that can be used to select delegates in a model instantiator (Repeater, ListView...).
6+
Interesection test is done in the coordinate system of the container Item, using delegate's bounding boxes.
7+
The list of selected indices is emitted when the selection ends.
8+
*/
9+
10+
SelectionBox {
11+
id: root
12+
13+
// The Item instantiating the delegates.
14+
property Item modelInstantiator
15+
// The Item containing the delegates (used for coordinate mapping).
16+
property Item container
17+
// Emitted when the selection has ended, with the list of selected indices and modifiers.
18+
signal delegateSelectionEnded(list<int> indices, int modifiers)
19+
20+
onSelectionEnded: function(selectionRect, modifiers) {
21+
let selectedIndices = [];
22+
const mappedSelectionRect = mapToItem(container, selectionRect);
23+
for (var i = 0; i < modelInstantiator.count; ++i) {
24+
const delegate = modelInstantiator.itemAt(i);
25+
const delegateRect = Qt.rect(delegate.x, delegate.y, delegate.width, delegate.height);
26+
if (Geom2D.rectRectIntersect(mappedSelectionRect, delegateRect)) {
27+
selectedIndices.push(i);
28+
}
29+
}
30+
delegateSelectionEnded(selectedIndices, modifiers);
31+
}
32+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import QtQuick
2+
3+
/*
4+
Simple selection box that can be used by a MouseArea.
5+
6+
Usage:
7+
1. Create a MouseArea and a SelectionBox.
8+
2. Bind the SelectionBox to the MouseArea by setting the `mouseArea` property.
9+
3. Call startSelection() with coordinates when the selection starts.
10+
4. Call endSelection() when the selection ends.
11+
5. Listen to the selectionEnded signal to get the selection rectangle.
12+
*/
13+
14+
Item {
15+
id: root
16+
17+
property MouseArea mouseArea
18+
property alias color: selectionBox.color
19+
property alias border: selectionBox.border
20+
21+
readonly property bool active: mouseArea.drag.target == dragTarget
22+
23+
signal selectionEnded(rect selectionRect, int modifiers)
24+
25+
function startSelection(mouse) {
26+
dragTarget.startPos.x = dragTarget.x = mouse.x;
27+
dragTarget.startPos.y = dragTarget.y = mouse.y;
28+
dragTarget.modifiers = mouse.modifiers;
29+
mouseArea.drag.target = dragTarget;
30+
}
31+
32+
function endSelection() {
33+
if (!active) {
34+
return;
35+
}
36+
mouseArea.drag.target = null;
37+
const rect = Qt.rect(selectionBox.x, selectionBox.y, selectionBox.width, selectionBox.height)
38+
selectionEnded(rect, dragTarget.modifiers);
39+
}
40+
41+
visible: active
42+
43+
Rectangle {
44+
id: selectionBox
45+
color: "#109b9b9b"
46+
border.width: 1
47+
border.color: "#b4b4b4"
48+
49+
x: Math.min(dragTarget.startPos.x, dragTarget.x)
50+
y: Math.min(dragTarget.startPos.y, dragTarget.y)
51+
width: Math.abs(dragTarget.x - dragTarget.startPos.x)
52+
height: Math.abs(dragTarget.y - dragTarget.startPos.y)
53+
}
54+
55+
Item {
56+
id: dragTarget
57+
property point startPos
58+
property var modifiers
59+
}
60+
}

meshroom/ui/qml/Utils/qmldir

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Request 1.0 request.js
66
Format 1.0 format.js
77
ErrorHandler 1.0 errorHandler.js
88
singleton ExifOrientation 1.0 ExifOrientation.qml
9+
SelectionBox 1.0 SelectionBox.qml
10+
DelegateSelectionBox 1.0 DelegateSelectionBox.qml
911
# using singleton here causes random crash at application exit
1012
# singleton Clipboard 1.0 Clipboard.qml
1113
# singleton Filepath 1.0 Filepath.qml

0 commit comments

Comments
 (0)