[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
/**
A component to display and edit a Node's attributes.
*/
ColumnLayout {
id: root
* A component to display and edit the attributes of a Node.
*/
property variant node: null // the node to edit
ListView {
id: root
property variant attributes: null
property bool readOnly: false
readonly property bool isCompatibilityNode: node.hasOwnProperty("compatibilityIssue")
property int labelWidth: 180
signal upgradeRequest()
signal attributeDoubleClicked(var attribute)
spacing: 0
implicitHeight: contentHeight
Pane {
Layout.fillWidth: true
background: Rectangle { color: Qt.darker(parent.palette.window, 1.15) }
padding: 2
clip: true
spacing: 2
ScrollBar.vertical: ScrollBar { id: scrollBar }
RowLayout {
width: parent.width
model: SortFilterDelegateModel {
Label {
Layout.fillWidth: true
elide: Text.ElideMiddle
text: node.label
horizontalAlignment: Text.AlignHCenter
padding: 6
}
model: attributes
filterRole: GraphEditorSettings.showAdvancedAttributes ? "" : "advanced"
filterValue: false
ToolButton {
text: MaterialIcons.settings
font.family: MaterialIcons.fontFamily
onClicked: settingsMenu.popup()
checkable: true
checked: settingsMenu.visible
}
function modelData(item, roleName) {
return item.model.object.desc[roleName]
}
Menu {
id: settingsMenu
MenuItem {
text: "Advanced Attributes"
checked: GraphEditorSettings.showAdvancedAttributes
onClicked: GraphEditorSettings.showAdvancedAttributes = !GraphEditorSettings.showAdvancedAttributes
}
MenuItem {
text: "Open Cache Folder"
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()
Component {
id: delegateComponent
AttributeItemDelegate {
width: ListView.view.width - scrollBar.width
readOnly: root.readOnly
labelWidth: root.labelWidth
attribute: object
onDoubleClicked: root.attributeDoubleClicked(attr)
}
}
}
// CompatibilityBadge banner for CompatibilityNode
Loader {
active: isCompatibilityNode
Layout.fillWidth: true
visible: active // for layout update
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
}
// Helper MouseArea to lose edit/activeFocus
// when clicking on the background
MouseArea {
anchors.fill: parent
onClicked: root.forceActiveFocus()
z: -1
}
}

View file

@ -348,37 +348,17 @@ RowLayout {
Component {
id: groupAttribute_component
ListView {
id: chilrenListView
implicitWidth: parent.width
implicitHeight: childrenRect.height
onCountChanged: forceLayout()
spacing: 2
model: SortFilterDelegateModel {
model: attribute.value
filterRole: GraphEditorSettings.showAdvancedAttributes ? "" : "advanced"
filterValue: false
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)})
}
}
ColumnLayout {
id: groupItem
Component.onCompleted: {
var cpt = Qt.createComponent("AttributeEditor.qml");
var obj = cpt.createObject(groupItem,
{'attributes': Qt.binding(function() { return attribute.value }),
'readOnly': Qt.binding(function() { return root.readOnly }),
'labelWidth': 100, // reduce label width for children (space gain)
})
obj.Layout.fillWidth = true;
obj.attributeDoubleClicked.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
GraphEditor 1.0 GraphEditor.qml
NodeEditor 1.0 NodeEditor.qml
Node 1.0 Node.qml
NodeChunks 1.0 NodeChunks.qml
Edge 1.0 Edge.qml

View file

@ -507,84 +507,65 @@ ApplicationWindow {
}
}
Panel {
id: graphEditorPanel
Layout.fillWidth: true
Layout.fillHeight: false
padding: 0
Controls1.SplitView {
orientation: Qt.Horizontal
width: parent.width
height: Math.round(parent.height * 0.3)
title: "Graph Editor"
visible: settings_UILayout.showGraphEditor
function displayAttribute(attr) {
if( attr.desc.type === "File"
&& _3dFileExtensions.indexOf(Filepath.extension(attr.value)) > - 1 )
{
workspaceView.viewAttribute(attr);
return true;
}
return false;
}
Panel {
id: graphEditorPanel
Layout.fillWidth: true
padding: 4
title: "Graph Editor"
visible: settings_UILayout.showGraphEditor
Controls1.SplitView {
orientation: Qt.Horizontal
anchors.fill: parent
function displayAttribute(attr) {
if( attr.desc.type === "File"
&& _3dFileExtensions.indexOf(Filepath.extension(attr.value)) > - 1 )
{
workspaceView.viewAttribute(attr);
return true;
}
return false;
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: 2
GraphEditor {
id: graphEditor
GraphEditor {
id: graphEditor
anchors.fill: parent
uigraph: _reconstruction
nodeTypesModel: _nodeTypes
readOnly: _reconstruction.computing
anchors.fill: parent
uigraph: _reconstruction
nodeTypesModel: _nodeTypes
readOnly: _reconstruction.computing
onNodeDoubleClicked: {
if(node.nodeType === "StructureFromMotion")
onNodeDoubleClicked: {
if(node.nodeType === "StructureFromMotion")
{
_reconstruction.sfm = node
return
}
for(var i=0; i < node.attributes.count; ++i)
{
var attr = node.attributes.at(i)
if(attr.isOutput
&& graphEditorPanel.displayAttribute(attr))
{
_reconstruction.sfm = node
return
}
for(var i=0; i < node.attributes.count; ++i)
{
var attr = node.attributes.at(i)
if(attr.isOutput
&& graphEditorPanel.displayAttribute(attr))
{
break;
}
break;
}
}
}
}
Item {
implicitHeight: Math.round(parent.height * 0.2)
implicitWidth: Math.round(parent.width * 0.3)
}
Loader {
anchors.fill: parent
anchors.margins: 2
active: _reconstruction.selectedNode !== null
sourceComponent: Component {
AttributeEditor {
node: _reconstruction.selectedNode
// Make AttributeEditor readOnly when computing
readOnly: _reconstruction.computing
onAttributeDoubleClicked: {
graphEditorPanel.displayAttribute(attribute)
}
onUpgradeRequest: {
var n = _reconstruction.upgradeNode(node)
_reconstruction.selectedNode = n;
}
}
}
}
NodeEditor {
width: Math.round(parent.width * 0.3)
node: _reconstruction.selectedNode
// Make NodeEditor readOnly when computing
readOnly: _reconstruction.computing
onAttributeDoubleClicked: graphEditorPanel.displayAttribute(attribute)
onUpgradeRequest: {
var n = _reconstruction.upgradeNode(node);
_reconstruction.selectedNode = n;
}
}
}