mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-03 12:16:51 +02:00
To be consistent with the Graph Editor behavior, double clicking on a node sets it as the active node of its type. This, for example, enables to switch from one SfM to another from the media list.
405 lines
18 KiB
QML
405 lines
18 KiB
QML
import QtQuick 2.7
|
|
import QtQuick.Controls 2.3
|
|
import QtQuick.Layouts 1.3
|
|
import MaterialIcons 2.2
|
|
import Qt3D.Core 2.0
|
|
import Qt3D.Render 2.1
|
|
import QtQuick.Controls.Material 2.4
|
|
import Controls 1.0
|
|
import Utils 1.0
|
|
|
|
FloatingPane {
|
|
id: root
|
|
|
|
implicitWidth: 200
|
|
|
|
property int renderMode: 2
|
|
property Grid3D grid: null
|
|
property MediaLibrary mediaLibrary
|
|
property Camera camera
|
|
property var uigraph: null
|
|
|
|
signal mediaFocusRequest(var index)
|
|
signal mediaRemoveRequest(var index)
|
|
signal nodeActivated(var node)
|
|
|
|
padding: 0
|
|
|
|
MouseArea { anchors.fill: parent; onWheel: wheel.accepted = true }
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: 4
|
|
|
|
Group {
|
|
Layout.fillWidth: true
|
|
title: "DISPLAY"
|
|
|
|
GridLayout {
|
|
width: parent.width
|
|
columns: 2
|
|
columnSpacing: 6
|
|
rowSpacing: 3
|
|
Flow {
|
|
Layout.columnSpan: 2
|
|
Layout.fillWidth: true
|
|
spacing: 1
|
|
MaterialToolButton {
|
|
text: MaterialIcons.grid_on
|
|
ToolTip.text: "Display Grid"
|
|
checked: Viewer3DSettings.displayGrid
|
|
onClicked: Viewer3DSettings.displayGrid = !Viewer3DSettings.displayGrid
|
|
}
|
|
MaterialToolButton {
|
|
text: MaterialIcons.adjust
|
|
checked: Viewer3DSettings.displayGizmo
|
|
ToolTip.text: "Display Trackball"
|
|
onClicked: Viewer3DSettings.displayGizmo = !Viewer3DSettings.displayGizmo
|
|
}
|
|
MaterialToolButton {
|
|
text: MaterialIcons.call_merge
|
|
ToolTip.text: "Display Origin"
|
|
checked: Viewer3DSettings.displayOrigin
|
|
onClicked: Viewer3DSettings.displayOrigin = !Viewer3DSettings.displayOrigin
|
|
}
|
|
}
|
|
MaterialLabel { text: MaterialIcons.grain; padding: 2 }
|
|
RowLayout {
|
|
Slider {
|
|
Layout.fillWidth: true; from: 0; to: 5; stepSize: 0.1
|
|
value: Viewer3DSettings.pointSize
|
|
onValueChanged: Viewer3DSettings.pointSize = value
|
|
ToolTip.text: "Point Size: " + value.toFixed(2)
|
|
ToolTip.visible: hovered || pressed
|
|
ToolTip.delay: 150
|
|
}
|
|
MaterialToolButton {
|
|
text: MaterialIcons.center_focus_strong
|
|
ToolTip.text: "Fixed Point Size"
|
|
font.pointSize: 10
|
|
padding: 3
|
|
checked: Viewer3DSettings.fixedPointSize
|
|
onClicked: Viewer3DSettings.fixedPointSize = !Viewer3DSettings.fixedPointSize
|
|
}
|
|
|
|
}
|
|
MaterialLabel { text: MaterialIcons.videocam; padding: 2 }
|
|
Slider {
|
|
value: Viewer3DSettings.cameraScale
|
|
from: 0
|
|
to: 2
|
|
stepSize: 0.01
|
|
Layout.fillWidth: true
|
|
padding: 0
|
|
onMoved: Viewer3DSettings.cameraScale = value
|
|
ToolTip.text: "Camera Scale: " + value.toFixed(2)
|
|
ToolTip.visible: hovered || pressed
|
|
ToolTip.delay: 150
|
|
}
|
|
}
|
|
}
|
|
|
|
Group {
|
|
Layout.fillWidth: true
|
|
title: "CAMERA"
|
|
ColumnLayout {
|
|
width: parent.width
|
|
|
|
// Image/Camera synchronization
|
|
Flow {
|
|
Layout.fillWidth: true
|
|
spacing: 2
|
|
// Synchronization
|
|
MaterialToolButton {
|
|
id: syncViewpointCamera
|
|
enabled: _reconstruction.sfmReport
|
|
text: MaterialIcons.linked_camera
|
|
ToolTip.text: "Sync with Image Selection"
|
|
checked: enabled && Viewer3DSettings.syncViewpointCamera
|
|
onClicked: Viewer3DSettings.syncViewpointCamera = !Viewer3DSettings.syncViewpointCamera
|
|
}
|
|
// Image Overlay controls
|
|
RowLayout {
|
|
visible: syncViewpointCamera.enabled && Viewer3DSettings.syncViewpointCamera
|
|
spacing: 2
|
|
// Activation
|
|
MaterialToolButton {
|
|
text: MaterialIcons.image
|
|
ToolTip.text: "Image Overlay"
|
|
checked: Viewer3DSettings.viewpointImageOverlay
|
|
onClicked: Viewer3DSettings.viewpointImageOverlay = !Viewer3DSettings.viewpointImageOverlay
|
|
}
|
|
// Opacity
|
|
Slider {
|
|
visible: Viewer3DSettings.showViewpointImageOverlay
|
|
implicitWidth: 60
|
|
from: 0
|
|
to: 100
|
|
value: Viewer3DSettings.viewpointImageOverlayOpacity * 100
|
|
onValueChanged: Viewer3DSettings.viewpointImageOverlayOpacity = value / 100
|
|
ToolTip.text: "Image Opacity: " + Viewer3DSettings.viewpointImageOverlayOpacity.toFixed(2)
|
|
ToolTip.visible: hovered || pressed
|
|
ToolTip.delay: 100
|
|
}
|
|
}
|
|
// Image Frame control
|
|
MaterialToolButton {
|
|
visible: syncViewpointCamera.enabled && Viewer3DSettings.showViewpointImageOverlay
|
|
enabled: Viewer3DSettings.syncViewpointCamera
|
|
text: MaterialIcons.crop_free
|
|
ToolTip.text: "Frame Overlay"
|
|
checked: Viewer3DSettings.viewpointImageFrame
|
|
onClicked: Viewer3DSettings.viewpointImageFrame = !Viewer3DSettings.viewpointImageFrame
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3D Scene content
|
|
Group {
|
|
title: "SCENE"
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
sidePadding: 0
|
|
|
|
toolBarContent: MaterialToolButton {
|
|
id: infoButton
|
|
ToolTip.text: "Media Info"
|
|
text: MaterialIcons.info_outline
|
|
font.pointSize: 10
|
|
implicitHeight: parent.height
|
|
checkable: true
|
|
checked: true
|
|
}
|
|
|
|
ListView {
|
|
id: mediaListView
|
|
anchors.fill: parent
|
|
clip: true
|
|
model: mediaLibrary.model
|
|
spacing: 4
|
|
|
|
ScrollBar.vertical: ScrollBar { id: scrollBar }
|
|
|
|
currentIndex: -1
|
|
|
|
Connections {
|
|
target: uigraph
|
|
onSelectedNodeChanged: mediaListView.currentIndex = -1
|
|
}
|
|
|
|
Connections {
|
|
target: mediaLibrary
|
|
onLoadRequest: {
|
|
mediaListView.positionViewAtIndex(idx, ListView.Visible);
|
|
}
|
|
}
|
|
|
|
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
|
|
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 }
|
|
}
|
|
|
|
height: childrenRect.height
|
|
width: parent.width - scrollBar.width
|
|
|
|
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;
|
|
nodeActivated(model.attribute.node);
|
|
}
|
|
|
|
RowLayout {
|
|
width: parent.width
|
|
spacing: 2
|
|
|
|
property string src: model.source
|
|
onSrcChanged: focusAnim.restart()
|
|
|
|
Connections {
|
|
target: mediaListView
|
|
onCountChanged: mediaDelegate.updateCurrentIndex()
|
|
}
|
|
|
|
// 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 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(hoverArea.modifiers & Qt.ControlModifier)
|
|
mediaLibrary.solo(index);
|
|
else
|
|
model.visible = !model.visible
|
|
}
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
visible: !loading && mediaDelegate.containsMouse
|
|
text: MaterialIcons.clear
|
|
font.pointSize: 10
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|