mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-28 17:57:16 +02:00
[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.
This commit is contained in:
parent
dfe2166942
commit
6d2e9a2ba9
5 changed files with 106 additions and 1 deletions
|
@ -1,11 +1,12 @@
|
|||
|
||||
def registerTypes():
|
||||
from PySide6.QtQml import qmlRegisterType
|
||||
from PySide6.QtQml import qmlRegisterType, qmlRegisterSingletonType
|
||||
from meshroom.ui.components.clipboard import ClipboardHelper
|
||||
from meshroom.ui.components.edge import EdgeMouseArea
|
||||
from meshroom.ui.components.filepath import FilepathHelper
|
||||
from meshroom.ui.components.scene3D import Scene3DHelper, TrackballController, Transformations3DHelper
|
||||
from meshroom.ui.components.csvData import CsvData
|
||||
from meshroom.ui.components.geom2D import Geom2D
|
||||
|
||||
qmlRegisterType(EdgeMouseArea, "GraphEditor", 1, 0, "EdgeMouseArea")
|
||||
qmlRegisterType(ClipboardHelper, "Meshroom.Helpers", 1, 0, "ClipboardHelper") # TODO: uncreatable
|
||||
|
@ -14,3 +15,5 @@ def registerTypes():
|
|||
qmlRegisterType(Transformations3DHelper, "Meshroom.Helpers", 1, 0, "Transformations3DHelper") # TODO: uncreatable
|
||||
qmlRegisterType(TrackballController, "Meshroom.Helpers", 1, 0, "TrackballController")
|
||||
qmlRegisterType(CsvData, "DataObjects", 1, 0, "CsvData")
|
||||
|
||||
qmlRegisterSingletonType(Geom2D, "Meshroom.Helpers", 1, 0, "Geom2D")
|
||||
|
|
8
meshroom/ui/components/geom2D.py
Normal file
8
meshroom/ui/components/geom2D.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from PySide6.QtCore import QObject, Slot, QRectF
|
||||
|
||||
|
||||
class Geom2D(QObject):
|
||||
@Slot(QRectF, QRectF, result=bool)
|
||||
def rectRectIntersect(self, rect1: QRectF, rect2: QRectF) -> bool:
|
||||
"""Check if two rectangles intersect."""
|
||||
return rect1.intersects(rect2)
|
32
meshroom/ui/qml/Controls/DelegateSelectionBox.qml
Normal file
32
meshroom/ui/qml/Controls/DelegateSelectionBox.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import QtQuick
|
||||
import Meshroom.Helpers
|
||||
|
||||
/*
|
||||
A SelectionBox that can be used to select delegates in a model instantiator (Repeater, ListView...).
|
||||
Interesection test is done in the coordinate system of the container Item, using delegate's bounding boxes.
|
||||
The list of selected indices is emitted when the selection ends.
|
||||
*/
|
||||
|
||||
SelectionBox {
|
||||
id: root
|
||||
|
||||
// The Item instantiating the delegates.
|
||||
property Item modelInstantiator
|
||||
// The Item containing the delegates (used for coordinate mapping).
|
||||
property Item container
|
||||
// Emitted when the selection has ended, with the list of selected indices and modifiers.
|
||||
signal delegateSelectionEnded(list<int> indices, int modifiers)
|
||||
|
||||
onSelectionEnded: function(selectionRect, modifiers) {
|
||||
let selectedIndices = [];
|
||||
const mappedSelectionRect = mapToItem(container, selectionRect);
|
||||
for (var i = 0; i < modelInstantiator.count; ++i) {
|
||||
const delegate = modelInstantiator.itemAt(i);
|
||||
const delegateRect = Qt.rect(delegate.x, delegate.y, delegate.width, delegate.height);
|
||||
if (Geom2D.rectRectIntersect(mappedSelectionRect, delegateRect)) {
|
||||
selectedIndices.push(i);
|
||||
}
|
||||
}
|
||||
delegateSelectionEnded(selectedIndices, modifiers);
|
||||
}
|
||||
}
|
60
meshroom/ui/qml/Controls/SelectionBox.qml
Normal file
60
meshroom/ui/qml/Controls/SelectionBox.qml
Normal file
|
@ -0,0 +1,60 @@
|
|||
import QtQuick
|
||||
|
||||
/*
|
||||
Simple selection box that can be used by a MouseArea.
|
||||
|
||||
Usage:
|
||||
1. Create a MouseArea and a SelectionBox.
|
||||
2. Bind the SelectionBox to the MouseArea by setting the `mouseArea` property.
|
||||
3. Call startSelection() with coordinates when the selection starts.
|
||||
4. Call endSelection() when the selection ends.
|
||||
5. Listen to the selectionEnded signal to get the selection rectangle.
|
||||
*/
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property MouseArea mouseArea
|
||||
property alias color: selectionBox.color
|
||||
property alias border: selectionBox.border
|
||||
|
||||
readonly property bool active: mouseArea.drag.target == dragTarget
|
||||
|
||||
signal selectionEnded(rect selectionRect, int modifiers)
|
||||
|
||||
function startSelection(mouse) {
|
||||
dragTarget.startPos.x = dragTarget.x = mouse.x;
|
||||
dragTarget.startPos.y = dragTarget.y = mouse.y;
|
||||
dragTarget.modifiers = mouse.modifiers;
|
||||
mouseArea.drag.target = dragTarget;
|
||||
}
|
||||
|
||||
function endSelection() {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
mouseArea.drag.target = null;
|
||||
const rect = Qt.rect(selectionBox.x, selectionBox.y, selectionBox.width, selectionBox.height)
|
||||
selectionEnded(rect, dragTarget.modifiers);
|
||||
}
|
||||
|
||||
visible: active
|
||||
|
||||
Rectangle {
|
||||
id: selectionBox
|
||||
color: "#109b9b9b"
|
||||
border.width: 1
|
||||
border.color: "#b4b4b4"
|
||||
|
||||
x: Math.min(dragTarget.startPos.x, dragTarget.x)
|
||||
y: Math.min(dragTarget.startPos.y, dragTarget.y)
|
||||
width: Math.abs(dragTarget.x - dragTarget.startPos.x)
|
||||
height: Math.abs(dragTarget.y - dragTarget.startPos.y)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: dragTarget
|
||||
property point startPos
|
||||
property var modifiers
|
||||
}
|
||||
}
|
|
@ -17,3 +17,5 @@ IntSelector 1.0 IntSelector.qml
|
|||
MScrollBar 1.0 MScrollBar.qml
|
||||
MSplitView 1.0 MSplitView.qml
|
||||
DirectionalLightPane 1.0 DirectionalLightPane.qml
|
||||
SelectionBox 1.0 SelectionBox.qml
|
||||
DelegateSelectionBox 1.0 DelegateSelectionBox.qml
|
Loading…
Add table
Reference in a new issue