mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-29 17:06:35 +02:00
[ui] extract NodeEditor from AttributeEditor
* NodeEditor * exposes Node parameters: compatibility, attributes and logs * provides a placeholder when no active Node * AttributeEditor * only displays the list of Attributes * use 'AttributeEditor' for GroupAttributes in AttributeItemDelegate * Layout * move NodeEditor on the same SplitView level as GraphEditor * move current node name and menu to the Panel's header
This commit is contained in:
parent
b6e4876494
commit
00feb46667
5 changed files with 245 additions and 232 deletions
|
@ -5,156 +5,52 @@ import MaterialIcons 2.2
|
||||||
import Utils 1.0
|
import Utils 1.0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A component to display and edit a Node's attributes.
|
* A component to display and edit the attributes of a Node.
|
||||||
*/
|
*/
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property variant node: null // the node to edit
|
ListView {
|
||||||
|
id: root
|
||||||
|
property variant attributes: null
|
||||||
property bool readOnly: false
|
property bool readOnly: false
|
||||||
readonly property bool isCompatibilityNode: node.hasOwnProperty("compatibilityIssue")
|
property int labelWidth: 180
|
||||||
|
|
||||||
signal upgradeRequest()
|
signal upgradeRequest()
|
||||||
|
|
||||||
signal attributeDoubleClicked(var attribute)
|
signal attributeDoubleClicked(var attribute)
|
||||||
|
|
||||||
spacing: 0
|
implicitHeight: contentHeight
|
||||||
|
|
||||||
Pane {
|
clip: true
|
||||||
Layout.fillWidth: true
|
spacing: 2
|
||||||
background: Rectangle { color: Qt.darker(parent.palette.window, 1.15) }
|
ScrollBar.vertical: ScrollBar { id: scrollBar }
|
||||||
padding: 2
|
|
||||||
|
|
||||||
RowLayout {
|
model: SortFilterDelegateModel {
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
Label {
|
model: attributes
|
||||||
Layout.fillWidth: true
|
filterRole: GraphEditorSettings.showAdvancedAttributes ? "" : "advanced"
|
||||||
elide: Text.ElideMiddle
|
filterValue: false
|
||||||
text: node.label
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
padding: 6
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolButton {
|
function modelData(item, roleName) {
|
||||||
text: MaterialIcons.settings
|
return item.model.object.desc[roleName]
|
||||||
font.family: MaterialIcons.fontFamily
|
|
||||||
onClicked: settingsMenu.popup()
|
|
||||||
checkable: true
|
|
||||||
checked: settingsMenu.visible
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Menu {
|
|
||||||
id: settingsMenu
|
Component {
|
||||||
MenuItem {
|
id: delegateComponent
|
||||||
text: "Advanced Attributes"
|
AttributeItemDelegate {
|
||||||
checked: GraphEditorSettings.showAdvancedAttributes
|
width: ListView.view.width - scrollBar.width
|
||||||
onClicked: GraphEditorSettings.showAdvancedAttributes = !GraphEditorSettings.showAdvancedAttributes
|
readOnly: root.readOnly
|
||||||
}
|
labelWidth: root.labelWidth
|
||||||
MenuItem {
|
attribute: object
|
||||||
text: "Open Cache Folder"
|
onDoubleClicked: root.attributeDoubleClicked(attr)
|
||||||
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(node.internalFolder))
|
|
||||||
ToolTip.text: node.internalFolder
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: 500
|
|
||||||
}
|
|
||||||
MenuSeparator {}
|
|
||||||
MenuItem {
|
|
||||||
text: "Clear Submitted Status"
|
|
||||||
onClicked: node.clearSubmittedChunks()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompatibilityBadge banner for CompatibilityNode
|
// Helper MouseArea to lose edit/activeFocus
|
||||||
Loader {
|
// when clicking on the background
|
||||||
active: isCompatibilityNode
|
MouseArea {
|
||||||
Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
visible: active // for layout update
|
onClicked: root.forceActiveFocus()
|
||||||
|
z: -1
|
||||||
sourceComponent: CompatibilityBadge {
|
|
||||||
canUpgrade: root.node.canUpgrade
|
|
||||||
issueDetails: root.node.issueDetails
|
|
||||||
onUpgradeRequest: root.upgradeRequest()
|
|
||||||
sourceComponent: bannerDelegate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
currentIndex: tabBar.currentIndex
|
|
||||||
|
|
||||||
Item {
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: attributesListView
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 2
|
|
||||||
clip: true
|
|
||||||
spacing: 2
|
|
||||||
ScrollBar.vertical: ScrollBar { id: scrollBar }
|
|
||||||
|
|
||||||
model: SortFilterDelegateModel {
|
|
||||||
|
|
||||||
model: node ? node.attributes : null
|
|
||||||
filterRole: GraphEditorSettings.showAdvancedAttributes ? "" : "advanced"
|
|
||||||
filterValue: false
|
|
||||||
function modelData(item, roleName) {
|
|
||||||
return item.model.object.desc[roleName]
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: delegateComponent
|
|
||||||
AttributeItemDelegate {
|
|
||||||
width: attributesListView.width - scrollBar.width
|
|
||||||
readOnly: root.isCompatibilityNode
|
|
||||||
labelWidth: 180
|
|
||||||
attribute: object
|
|
||||||
onDoubleClicked: root.attributeDoubleClicked(attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper MouseArea to lose edit/activeFocus
|
|
||||||
// when clicking on the background
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: root.forceActiveFocus()
|
|
||||||
z: -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeLog {
|
|
||||||
id: nodeLog
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
node: root.node
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TabBar {
|
|
||||||
id: tabBar
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
width: childrenRect.width
|
|
||||||
position: TabBar.Footer
|
|
||||||
TabButton {
|
|
||||||
text: "Attributes"
|
|
||||||
width: implicitWidth
|
|
||||||
padding: 4
|
|
||||||
leftPadding: 8
|
|
||||||
rightPadding: leftPadding
|
|
||||||
}
|
|
||||||
TabButton {
|
|
||||||
text: "Log"
|
|
||||||
width: implicitWidth
|
|
||||||
leftPadding: 8
|
|
||||||
rightPadding: leftPadding
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -348,37 +348,17 @@ RowLayout {
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: groupAttribute_component
|
id: groupAttribute_component
|
||||||
ListView {
|
ColumnLayout {
|
||||||
id: chilrenListView
|
id: groupItem
|
||||||
implicitWidth: parent.width
|
Component.onCompleted: {
|
||||||
implicitHeight: childrenRect.height
|
var cpt = Qt.createComponent("AttributeEditor.qml");
|
||||||
onCountChanged: forceLayout()
|
var obj = cpt.createObject(groupItem,
|
||||||
spacing: 2
|
{'attributes': Qt.binding(function() { return attribute.value }),
|
||||||
model: SortFilterDelegateModel {
|
'readOnly': Qt.binding(function() { return root.readOnly }),
|
||||||
model: attribute.value
|
'labelWidth': 100, // reduce label width for children (space gain)
|
||||||
filterRole: GraphEditorSettings.showAdvancedAttributes ? "" : "advanced"
|
})
|
||||||
filterValue: false
|
obj.Layout.fillWidth = true;
|
||||||
|
obj.attributeDoubleClicked.connect(function(attr) {root.doubleClicked(attr)})
|
||||||
function modelData(item, roleName) {
|
|
||||||
return item.model.object.desc[roleName]
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: RowLayout {
|
|
||||||
id: row
|
|
||||||
width: chilrenListView.width
|
|
||||||
property var childAttrib: object
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
var cpt = Qt.createComponent("AttributeItemDelegate.qml")
|
|
||||||
var obj = cpt.createObject(row,
|
|
||||||
{'attribute': Qt.binding(function() { return row.childAttrib }),
|
|
||||||
'readOnly': Qt.binding(function() { return root.readOnly })
|
|
||||||
})
|
|
||||||
obj.Layout.fillWidth = true
|
|
||||||
obj.labelWidth = 100 // reduce label width for children (space gain)
|
|
||||||
obj.doubleClicked.connect(function(attr) {root.doubleClicked(attr)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
155
meshroom/ui/qml/GraphEditor/NodeEditor.qml
Normal file
155
meshroom/ui/qml/GraphEditor/NodeEditor.qml
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import MaterialIcons 2.2
|
||||||
|
import Controls 1.0
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NodeEditor allows to visualize and edit the parameters of a Node.
|
||||||
|
* It mainly provides an attribute editor and a log inspector.
|
||||||
|
*/
|
||||||
|
Panel {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property variant node
|
||||||
|
property bool readOnly: false
|
||||||
|
property bool isCompatibilityNode: node && node.compatibilityIssue !== undefined
|
||||||
|
|
||||||
|
signal attributeDoubleClicked(var attribute)
|
||||||
|
signal upgradeRequest()
|
||||||
|
|
||||||
|
title: "Node" + (node !== null ? " - <b>" + node.label + "</b>" : "")
|
||||||
|
icon: MaterialLabel { text: MaterialIcons.tune }
|
||||||
|
|
||||||
|
headerBar: RowLayout {
|
||||||
|
MaterialToolButton {
|
||||||
|
text: MaterialIcons.more_vert
|
||||||
|
font.pointSize: 11
|
||||||
|
padding: 2
|
||||||
|
onClicked: settingsMenu.open()
|
||||||
|
checkable: true
|
||||||
|
checked: settingsMenu.visible
|
||||||
|
Menu {
|
||||||
|
id: settingsMenu
|
||||||
|
y: parent.height
|
||||||
|
MenuItem {
|
||||||
|
id: advancedToggle
|
||||||
|
text: "Advanced Attributes"
|
||||||
|
MaterialLabel {
|
||||||
|
anchors.right: parent.right; anchors.rightMargin: parent.padding;
|
||||||
|
text: MaterialIcons.build
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pointSize: 8
|
||||||
|
}
|
||||||
|
checkable: true
|
||||||
|
checked: GraphEditorSettings.showAdvancedAttributes
|
||||||
|
onClicked: GraphEditorSettings.showAdvancedAttributes = !GraphEditorSettings.showAdvancedAttributes
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: "Open Cache Folder"
|
||||||
|
enabled: root.node !== null
|
||||||
|
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(root.node.internalFolder))
|
||||||
|
}
|
||||||
|
MenuSeparator {}
|
||||||
|
MenuItem {
|
||||||
|
enabled: root.node !== null
|
||||||
|
text: "Clear Submitted Status"
|
||||||
|
onClicked: node.clearSubmittedChunks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// CompatibilityBadge banner for CompatibilityNode
|
||||||
|
Loader {
|
||||||
|
active: root.isCompatibilityNode
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: active // for layout update
|
||||||
|
|
||||||
|
sourceComponent: CompatibilityBadge {
|
||||||
|
canUpgrade: root.node.canUpgrade
|
||||||
|
issueDetails: root.node.issueDetails
|
||||||
|
onUpgradeRequest: root.upgradeRequest()
|
||||||
|
sourceComponent: bannerDelegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
sourceComponent: root.node ? editor_component : placeholder_component
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: placeholder_component
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
MaterialLabel {
|
||||||
|
text: MaterialIcons.select_all
|
||||||
|
font.pointSize: 34
|
||||||
|
color: Qt.lighter(palette.mid, 1.2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
color: Qt.lighter(palette.mid, 1.2)
|
||||||
|
text: "Select a Node to edit its Attributes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: editor_component
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
StackLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
currentIndex: tabBar.currentIndex
|
||||||
|
|
||||||
|
AttributeEditor {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
attributes: root.node.attributes
|
||||||
|
readOnly: root.isCompatibilityNode
|
||||||
|
onAttributeDoubleClicked: root.attributeDoubleClicked(attribute)
|
||||||
|
onUpgradeRequest: root.upgradeRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeLog {
|
||||||
|
id: nodeLog
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
node: root.node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TabBar {
|
||||||
|
id: tabBar
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
width: childrenRect.width
|
||||||
|
position: TabBar.Footer
|
||||||
|
TabButton {
|
||||||
|
text: "Attributes"
|
||||||
|
width: implicitWidth
|
||||||
|
padding: 4
|
||||||
|
leftPadding: 8
|
||||||
|
rightPadding: leftPadding
|
||||||
|
}
|
||||||
|
TabButton {
|
||||||
|
text: "Log"
|
||||||
|
width: implicitWidth
|
||||||
|
leftPadding: 8
|
||||||
|
rightPadding: leftPadding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
module GraphEditor
|
module GraphEditor
|
||||||
|
|
||||||
GraphEditor 1.0 GraphEditor.qml
|
GraphEditor 1.0 GraphEditor.qml
|
||||||
|
NodeEditor 1.0 NodeEditor.qml
|
||||||
Node 1.0 Node.qml
|
Node 1.0 Node.qml
|
||||||
NodeChunks 1.0 NodeChunks.qml
|
NodeChunks 1.0 NodeChunks.qml
|
||||||
Edge 1.0 Edge.qml
|
Edge 1.0 Edge.qml
|
||||||
|
|
|
@ -507,84 +507,65 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Panel {
|
Controls1.SplitView {
|
||||||
id: graphEditorPanel
|
orientation: Qt.Horizontal
|
||||||
Layout.fillWidth: true
|
width: parent.width
|
||||||
Layout.fillHeight: false
|
|
||||||
padding: 0
|
|
||||||
height: Math.round(parent.height * 0.3)
|
height: Math.round(parent.height * 0.3)
|
||||||
title: "Graph Editor"
|
|
||||||
visible: settings_UILayout.showGraphEditor
|
visible: settings_UILayout.showGraphEditor
|
||||||
|
|
||||||
function displayAttribute(attr) {
|
Panel {
|
||||||
if( attr.desc.type === "File"
|
id: graphEditorPanel
|
||||||
&& _3dFileExtensions.indexOf(Filepath.extension(attr.value)) > - 1 )
|
Layout.fillWidth: true
|
||||||
{
|
padding: 4
|
||||||
workspaceView.viewAttribute(attr);
|
title: "Graph Editor"
|
||||||
return true;
|
visible: settings_UILayout.showGraphEditor
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Controls1.SplitView {
|
function displayAttribute(attr) {
|
||||||
orientation: Qt.Horizontal
|
if( attr.desc.type === "File"
|
||||||
anchors.fill: parent
|
&& _3dFileExtensions.indexOf(Filepath.extension(attr.value)) > - 1 )
|
||||||
|
{
|
||||||
|
workspaceView.viewAttribute(attr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
GraphEditor {
|
||||||
Layout.fillHeight: true
|
id: graphEditor
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.margins: 2
|
|
||||||
|
|
||||||
GraphEditor {
|
anchors.fill: parent
|
||||||
id: graphEditor
|
uigraph: _reconstruction
|
||||||
|
nodeTypesModel: _nodeTypes
|
||||||
|
readOnly: _reconstruction.computing
|
||||||
|
|
||||||
anchors.fill: parent
|
onNodeDoubleClicked: {
|
||||||
uigraph: _reconstruction
|
if(node.nodeType === "StructureFromMotion")
|
||||||
nodeTypesModel: _nodeTypes
|
{
|
||||||
readOnly: _reconstruction.computing
|
_reconstruction.sfm = node
|
||||||
|
return
|
||||||
onNodeDoubleClicked: {
|
}
|
||||||
if(node.nodeType === "StructureFromMotion")
|
for(var i=0; i < node.attributes.count; ++i)
|
||||||
|
{
|
||||||
|
var attr = node.attributes.at(i)
|
||||||
|
if(attr.isOutput
|
||||||
|
&& graphEditorPanel.displayAttribute(attr))
|
||||||
{
|
{
|
||||||
_reconstruction.sfm = node
|
break;
|
||||||
return
|
|
||||||
}
|
|
||||||
for(var i=0; i < node.attributes.count; ++i)
|
|
||||||
{
|
|
||||||
var attr = node.attributes.at(i)
|
|
||||||
if(attr.isOutput
|
|
||||||
&& graphEditorPanel.displayAttribute(attr))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item {
|
}
|
||||||
implicitHeight: Math.round(parent.height * 0.2)
|
|
||||||
implicitWidth: Math.round(parent.width * 0.3)
|
|
||||||
|
|
||||||
Loader {
|
NodeEditor {
|
||||||
anchors.fill: parent
|
width: Math.round(parent.width * 0.3)
|
||||||
anchors.margins: 2
|
node: _reconstruction.selectedNode
|
||||||
active: _reconstruction.selectedNode !== null
|
// Make NodeEditor readOnly when computing
|
||||||
sourceComponent: Component {
|
readOnly: _reconstruction.computing
|
||||||
AttributeEditor {
|
onAttributeDoubleClicked: graphEditorPanel.displayAttribute(attribute)
|
||||||
node: _reconstruction.selectedNode
|
onUpgradeRequest: {
|
||||||
// Make AttributeEditor readOnly when computing
|
var n = _reconstruction.upgradeNode(node);
|
||||||
readOnly: _reconstruction.computing
|
_reconstruction.selectedNode = n;
|
||||||
onAttributeDoubleClicked: {
|
|
||||||
graphEditorPanel.displayAttribute(attribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpgradeRequest: {
|
|
||||||
var n = _reconstruction.upgradeNode(node)
|
|
||||||
_reconstruction.selectedNode = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue