[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:
Yann Lanthony 2018-12-14 16:02:04 +01:00
parent b6e4876494
commit 00feb46667
5 changed files with 245 additions and 232 deletions

View file

@ -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
}
} }
} }

View file

@ -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)})
}
}
} }
} }
} }

View 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
}
}
}
}
}
}
}

View file

@ -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

View file

@ -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;
}
}
}
}
} }
} }
} }