mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-08-03 16:58:24 +02:00
[ui] Inspector3D: simplify handling of scene media items hovering
* add MouseArea at root of media * use flow to display media info and avoid text clipping
This commit is contained in:
parent
312b9f5e0b
commit
03e91bea49
1 changed files with 173 additions and 150 deletions
|
@ -21,6 +21,7 @@ FloatingPane {
|
|||
|
||||
signal mediaFocusRequest(var index)
|
||||
signal mediaRemoveRequest(var index)
|
||||
signal nodeActivated(var node)
|
||||
|
||||
padding: 0
|
||||
|
||||
|
@ -194,186 +195,208 @@ FloatingPane {
|
|||
}
|
||||
}
|
||||
|
||||
delegate: RowLayout {
|
||||
delegate: MouseArea {
|
||||
id: mediaDelegate
|
||||
// add mediaLibrary.count in the binding to ensure 'entity'
|
||||
// is re-evaluated when mediaLibrary delegates are modified
|
||||
property bool loading: model.status === SceneLoader.Loading
|
||||
spacing: 2
|
||||
width: parent.width - scrollBar.width / 2
|
||||
|
||||
property string src: model.source
|
||||
onSrcChanged: focusAnim.restart()
|
||||
|
||||
property bool hovered: model.attribute ? uigraph.hoveredNode === model.attribute.node : mouseArea.containsMouse
|
||||
property bool hovered: model.attribute ? uigraph.hoveredNode === model.attribute.node : containsMouse
|
||||
property bool isSelectedNode: model.attribute ? uigraph.selectedNode === model.attribute.node : false
|
||||
|
||||
onIsSelectedNodeChanged: updateCurrentIndex()
|
||||
|
||||
function updateCurrentIndex() {
|
||||
if(isSelectedNode) { mediaListView.currentIndex = index }
|
||||
}
|
||||
|
||||
onIsSelectedNodeChanged: updateCurrentIndex()
|
||||
height: childrenRect.height
|
||||
width: parent.width - scrollBar.width
|
||||
|
||||
Connections {
|
||||
target: mediaListView
|
||||
onCountChanged: mediaDelegate.updateCurrentIndex()
|
||||
hoverEnabled: true
|
||||
onEntered: { if(model.attribute) uigraph.hoveredNode = model.attribute.node }
|
||||
onExited: { if(model.attribute) uigraph.hoveredNode = null }
|
||||
onClicked: {
|
||||
if(model.attribute)
|
||||
uigraph.selectedNode = model.attribute.node;
|
||||
else
|
||||
uigraph.selectedNode = null;
|
||||
if(mouse.button == Qt.RightButton)
|
||||
contextMenu.popup();
|
||||
mediaListView.currentIndex = index;
|
||||
}
|
||||
onDoubleClicked: {
|
||||
model.visible = true;
|
||||
camera.viewEntity(root.mediaLibrary.entityAt(index));
|
||||
}
|
||||
|
||||
// Current/selected element indicator
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 2
|
||||
color: {
|
||||
if(mediaListView.currentIndex == index || mediaDelegate.isSelectedNode)
|
||||
return label.palette.highlight;
|
||||
if(mediaDelegate.hovered)
|
||||
return Qt.darker(label.palette.highlight, 1.5);
|
||||
return "transparent";
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
|
||||
// Media visibility/loading control
|
||||
MaterialToolButton {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
text: model.visible ? MaterialIcons.visibility : MaterialIcons.visibility_off
|
||||
font.pointSize: 10
|
||||
ToolTip.text: model.visible ? "Hide" : model.requested ? "Show" : model.valid ? "Load and Show" : "Load and Show when Available"
|
||||
flat: true
|
||||
opacity: model.visible ? 1.0 : 0.6
|
||||
onClicked: {
|
||||
if(hoverArea.modifiers & Qt.ControlModifier)
|
||||
mediaLibrary.solo(index);
|
||||
else
|
||||
model.visible = !model.visible
|
||||
property string src: model.source
|
||||
onSrcChanged: focusAnim.restart()
|
||||
|
||||
Connections {
|
||||
target: mediaListView
|
||||
onCountChanged: mediaDelegate.updateCurrentIndex()
|
||||
}
|
||||
// Handle modifiers on button click
|
||||
MouseArea {
|
||||
id: hoverArea
|
||||
property int modifiers
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onPositionChanged: modifiers = mouse.modifiers
|
||||
onExited: modifiers = Qt.NoModifier
|
||||
onPressed: {
|
||||
modifiers = mouse.modifiers;
|
||||
mouse.accepted = false;
|
||||
|
||||
// Current/selected element indicator
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 2
|
||||
color: {
|
||||
if(mediaListView.currentIndex == index || mediaDelegate.isSelectedNode)
|
||||
return label.palette.highlight;
|
||||
if(mediaDelegate.hovered)
|
||||
return Qt.darker(label.palette.highlight, 1.5);
|
||||
return "transparent";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Media label and info
|
||||
Item {
|
||||
implicitHeight: childrenRect.height
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
ColumnLayout {
|
||||
id: centralLayout
|
||||
width: parent.width
|
||||
spacing: 1
|
||||
|
||||
Label {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
topPadding: 3
|
||||
bottomPadding: topPadding
|
||||
text: model.label
|
||||
opacity: model.valid ? 1.0 : 0.6
|
||||
elide: Text.ElideMiddle
|
||||
font.weight: mediaListView.currentIndex == index ? Font.DemiBold : Font.Normal
|
||||
background: Rectangle {
|
||||
Connections {
|
||||
target: mediaLibrary
|
||||
onLoadRequest: if(idx == index) focusAnim.restart()
|
||||
}
|
||||
ColorAnimation on color {
|
||||
id: focusAnim
|
||||
from: label.palette.highlight
|
||||
to: "transparent"
|
||||
duration: 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
visible: infoButton.checked
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: childrenRect.height
|
||||
RowLayout {
|
||||
visible: model.status === SceneLoader.Ready
|
||||
MaterialLabel { visible: model.vertexCount; text: MaterialIcons.grain }
|
||||
Label { visible: model.vertexCount; text: Format.intToString(model.vertexCount) }
|
||||
MaterialLabel { visible: model.faceCount; text: MaterialIcons.details; rotation: -180 }
|
||||
Label { visible: model.faceCount; text: Format.intToString(model.faceCount) }
|
||||
MaterialLabel { visible: model.cameraCount; text: MaterialIcons.videocam }
|
||||
Label { visible: model.cameraCount; text: model.cameraCount }
|
||||
MaterialLabel { visible: model.textureCount; text: MaterialIcons.texture }
|
||||
Label { visible: model.textureCount; text: model.textureCount }
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: centralLayout
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onEntered: { if(model.attribute) uigraph.hoveredNode = model.attribute.node }
|
||||
onExited: { if(model.attribute) uigraph.hoveredNode = null }
|
||||
// Media visibility/loading control
|
||||
MaterialToolButton {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
text: model.visible ? MaterialIcons.visibility : MaterialIcons.visibility_off
|
||||
font.pointSize: 10
|
||||
ToolTip.text: model.visible ? "Hide" : model.requested ? "Show" : model.valid ? "Load and Show" : "Load and Show when Available"
|
||||
flat: true
|
||||
opacity: model.visible ? 1.0 : 0.6
|
||||
onClicked: {
|
||||
if(model.attribute)
|
||||
uigraph.selectedNode = model.attribute.node;
|
||||
if(hoverArea.modifiers & Qt.ControlModifier)
|
||||
mediaLibrary.solo(index);
|
||||
else
|
||||
uigraph.selectedNode = null;
|
||||
if(mouse.button == Qt.RightButton)
|
||||
contextMenu.popup();
|
||||
mediaListView.currentIndex = index;
|
||||
model.visible = !model.visible
|
||||
}
|
||||
onDoubleClicked: {
|
||||
model.visible = true;
|
||||
camera.viewEntity(mediaLibrary.entityAt(index));
|
||||
// Handle modifiers on button click
|
||||
MouseArea {
|
||||
id: hoverArea
|
||||
property int modifiers
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onPositionChanged: modifiers = mouse.modifiers
|
||||
onExited: modifiers = Qt.NoModifier
|
||||
onPressed: {
|
||||
modifiers = mouse.modifiers;
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
MenuItem {
|
||||
text: "Open Containing Folder"
|
||||
enabled: model.valid
|
||||
onTriggered: Qt.openUrlExternally(Filepath.dirname(model.source))
|
||||
// Media label and info
|
||||
Item {
|
||||
implicitHeight: childrenRect.height
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
ColumnLayout {
|
||||
id: centralLayout
|
||||
width: parent.width
|
||||
spacing: 1
|
||||
|
||||
Label {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
topPadding: 3
|
||||
bottomPadding: topPadding
|
||||
text: model.label
|
||||
opacity: model.valid ? 1.0 : 0.6
|
||||
elide: Text.ElideMiddle
|
||||
font.weight: mediaListView.currentIndex == index ? Font.DemiBold : Font.Normal
|
||||
background: Rectangle {
|
||||
Connections {
|
||||
target: mediaLibrary
|
||||
onLoadRequest: if(idx == index) focusAnim.restart()
|
||||
}
|
||||
ColorAnimation on color {
|
||||
id: focusAnim
|
||||
from: label.palette.highlight
|
||||
to: "transparent"
|
||||
duration: 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
visible: infoButton.checked
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: childrenRect.height
|
||||
Flow {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
visible: model.status === SceneLoader.Ready
|
||||
RowLayout {
|
||||
spacing: 1
|
||||
visible: model.vertexCount
|
||||
MaterialLabel { text: MaterialIcons.grain }
|
||||
Label { text: Format.intToString(model.vertexCount) }
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 1
|
||||
visible: model.faceCount
|
||||
MaterialLabel { text: MaterialIcons.details; rotation: -180 }
|
||||
Label { text: Format.intToString(model.faceCount) }
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 1
|
||||
visible: model.cameraCount
|
||||
MaterialLabel { text: MaterialIcons.videocam }
|
||||
Label { text: model.cameraCount }
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 1
|
||||
visible: model.textureCount
|
||||
MaterialLabel { text: MaterialIcons.texture }
|
||||
Label { text: model.textureCount }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Copy Path"
|
||||
onTriggered: Clipboard.setText(Filepath.normpath(model.source))
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuItem {
|
||||
text: model.requested ? "Unload Media" : "Load Media"
|
||||
enabled: model.valid
|
||||
onTriggered: model.requested = !model.requested
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
MenuItem {
|
||||
text: "Open Containing Folder"
|
||||
enabled: model.valid
|
||||
onTriggered: Qt.openUrlExternally(Filepath.dirname(model.source))
|
||||
}
|
||||
MenuItem {
|
||||
text: "Copy Path"
|
||||
onTriggered: Clipboard.setText(Filepath.normpath(model.source))
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuItem {
|
||||
text: model.requested ? "Unload Media" : "Load Media"
|
||||
enabled: model.valid
|
||||
onTriggered: model.requested = !model.requested
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove media from library button
|
||||
MaterialToolButton {
|
||||
id: removeButton
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
// Remove media from library button
|
||||
MaterialToolButton {
|
||||
id: removeButton
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
|
||||
visible: hovered || mouseArea.containsMouse && !loading
|
||||
text: MaterialIcons.clear
|
||||
font.pointSize: 10
|
||||
ToolTip.text: "Remove"
|
||||
onClicked: mediaLibrary.remove(index)
|
||||
}
|
||||
visible: !loading && mediaDelegate.containsMouse
|
||||
text: MaterialIcons.clear
|
||||
font.pointSize: 10
|
||||
|
||||
// Media loading indicator
|
||||
BusyIndicator {
|
||||
visible: loading
|
||||
running: visible
|
||||
padding: removeButton.padding
|
||||
implicitHeight: implicitWidth
|
||||
implicitWidth: removeButton.width
|
||||
ToolTip.text: "Remove"
|
||||
ToolTip.delay: 500
|
||||
onClicked: mediaLibrary.remove(index)
|
||||
}
|
||||
|
||||
// Media loading indicator
|
||||
BusyIndicator {
|
||||
visible: loading
|
||||
running: visible
|
||||
padding: removeButton.padding
|
||||
implicitHeight: implicitWidth
|
||||
implicitWidth: removeButton.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue