diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py index 2e6059ff5b..e9ab67d54f 100644 --- a/meshroom/core/attribute.py +++ b/meshroom/core/attribute.py @@ -111,6 +111,10 @@ def getBaseType(self): def getLabel(self): return self._label + + @Slot(str, result=bool) + def matchText(self, text): + return self.fullLabel.lower().find(text.lower()) > -1 def getFullLabel(self): """ Full Label includes the name of all parent groups, e.g. 'groupLabel subGroupLabel Label' """ @@ -349,6 +353,7 @@ def updateInternals(self): isOutput = Property(bool, isOutput.fget, constant=True) isLinkChanged = Signal() isLink = Property(bool, isLink.fget, notify=isLinkChanged) + isLinkNested = isLink hasOutputConnectionsChanged = Signal() hasOutputConnections = Property(bool, hasOutputConnections.fget, notify=hasOutputConnectionsChanged) isDefault = Property(bool, _isDefault, notify=valueChanged) @@ -502,10 +507,19 @@ def updateInternals(self): for attr in self._value: attr.updateInternals() + @property + def isLinkNested(self): + """ Whether the attribute or any of its elements is a link to another attribute. """ + # note: directly use self.node.graph._edges to avoid using the property that may become invalid at some point + return self.isLink \ + or self.node.graph and self.isInput and self.node.graph._edges \ + and any(v in self.node.graph._edges.keys() for v in self._value) + # Override value property setter value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged) isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged) baseType = Property(str, getBaseType, constant=True) + isLinkNested = Property(bool, isLinkNested.fget) class GroupAttribute(Attribute): @@ -622,6 +636,10 @@ def updateInternals(self): for attr in self._value: attr.updateInternals() + @Slot(str, result=bool) + def matchText(self, text): + return super().matchText(text) or any(c.matchText(text) for c in self._value) + # Override value property value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged) isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged) diff --git a/meshroom/ui/qml/GraphEditor/AttributeEditor.qml b/meshroom/ui/qml/GraphEditor/AttributeEditor.qml index 593aa7be88..b421b7a5a4 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeEditor.qml @@ -12,6 +12,8 @@ ListView { id: root property bool readOnly: false property int labelWidth: 180 + property bool objectsHideable: true + property string filterText: "" signal upgradeRequest() signal attributeDoubleClicked(var mouse, var attribute) @@ -23,13 +25,21 @@ ListView { ScrollBar.vertical: ScrollBar { id: scrollBar } delegate: Loader { - active: object.enabled && (!object.desc.advanced || GraphEditorSettings.showAdvancedAttributes) + active: object.enabled && ( + !objectsHideable + || ((!object.desc.advanced || GraphEditorSettings.showAdvancedAttributes) + && (object.isDefault && GraphEditorSettings.showDefaultAttributes || !object.isDefault && GraphEditorSettings.showModifiedAttributes) + && (object.isOutput && GraphEditorSettings.showOutputAttributes || !object.isOutput && GraphEditorSettings.showInputAttributes) + && (object.isLinkNested && GraphEditorSettings.showLinkAttributes || !object.isLink && GraphEditorSettings.showNotLinkAttributes)) + ) && object.matchText(filterText) visible: active sourceComponent: AttributeItemDelegate { width: root.width - scrollBar.width readOnly: root.readOnly labelWidth: root.labelWidth + filterText: root.filterText + objectsHideable: root.objectsHideable attribute: object onDoubleClicked: root.attributeDoubleClicked(mouse, attr) } diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 496ee33e8b..f64fc6fa27 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -13,6 +13,8 @@ RowLayout { property variant attribute: null property bool readOnly: false // whether the attribute's value can be modified + property bool objectsHideable: true + property string filterText: "" property alias label: parameterLabel // accessor to the internal Label (attribute's name) property int labelWidth // shortcut to set the fixed size of the Label @@ -489,32 +491,39 @@ RowLayout { ScrollBar.vertical: ScrollBar { id: sb } - delegate: RowLayout { - id: item - property var childAttrib: object - layoutDirection: Qt.RightToLeft - width: lv.width - sb.width - Component.onCompleted: { - var cpt = Qt.createComponent("AttributeItemDelegate.qml") - var obj = cpt.createObject(item, - {'attribute': Qt.binding(function() { return item.childAttrib }), - 'readOnly': Qt.binding(function() { return !root.editable }) - }) - obj.Layout.fillWidth = true - obj.label.text = index - obj.label.horizontalAlignment = Text.AlignHCenter - obj.label.verticalAlignment = Text.AlignVCenter - obj.doubleClicked.connect(function(attr) {root.doubleClicked(attr)}) - } - ToolButton { - enabled: root.editable - text: MaterialIcons.remove_circle_outline - font.family: MaterialIcons.fontFamily - font.pointSize: 11 - padding: 2 - ToolTip.text: "Remove Element" - ToolTip.visible: hovered - onClicked: _reconstruction.removeAttribute(item.childAttrib) + delegate: Loader{ + active: !objectsHideable + || ((object.isDefault && GraphEditorSettings.showDefaultAttributes || !object.isDefault && GraphEditorSettings.showModifiedAttributes) + && (object.isLinkNested && GraphEditorSettings.showLinkAttributes || !object.isLinkNested && GraphEditorSettings.showNotLinkAttributes)) + visible: active + height: item ? implicitHeight : -spacing // compensate for spacing if item is hidden + sourceComponent: RowLayout { + id: item + property var childAttrib: object + layoutDirection: Qt.RightToLeft + width: lv.width - sb.width + Component.onCompleted: { + var cpt = Qt.createComponent("AttributeItemDelegate.qml") + var obj = cpt.createObject(item, + {'attribute': Qt.binding(function() { return item.childAttrib }), + 'readOnly': Qt.binding(function() { return !root.editable }) + }) + obj.Layout.fillWidth = true + obj.label.text = index + obj.label.horizontalAlignment = Text.AlignHCenter + obj.label.verticalAlignment = Text.AlignVCenter + obj.doubleClicked.connect(function(attr) {root.doubleClicked(attr)}) + } + ToolButton { + enabled: root.editable + text: MaterialIcons.remove_circle_outline + font.family: MaterialIcons.fontFamily + font.pointSize: 11 + padding: 2 + ToolTip.text: "Remove Element" + ToolTip.visible: hovered + onClicked: _reconstruction.removeAttribute(item.childAttrib) + } } } } @@ -531,6 +540,8 @@ RowLayout { {'model': Qt.binding(function() { return attribute.value }), 'readOnly': Qt.binding(function() { return root.readOnly }), 'labelWidth': 100, // reduce label width for children (space gain) + 'objectsHideable': Qt.binding(function() { return root.objectsHideable }), + 'filterText': Qt.binding(function() { return root.filterText }), }) obj.Layout.fillWidth = true; obj.attributeDoubleClicked.connect(function(attr) {root.doubleClicked(attr)}) diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml index e73b1383b7..a780573f9c 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -772,6 +772,86 @@ Item { } } + // Graph Nodes Search + FloatingPane { + id: navigation + padding: 2 + anchors.top: parent.top + + property int currentIndex: -1 + + RowLayout { + spacing: 0 + + SearchBar { + id: graphSearchBar + Layout.minimumWidth: 150 + width: 150 + textField.background.opacity: 0.5 + textField.onTextChanged: navigation.currentIndex = -1 + } + + MaterialToolButton { + text: MaterialIcons.arrow_left + padding: 0 + enabled: graphSearchBar.text !== "" + onClicked: { + navigation.currentIndex--; + if (navigation.currentIndex === -1) + navigation.currentIndex = filteredNodes.count - 1; + navigation.nextItem(); + } + } + + MaterialToolButton { + text: MaterialIcons.arrow_right + padding: 0 + enabled: graphSearchBar.text !== "" + onClicked: { + navigation.currentIndex++; + if (navigation.currentIndex === filteredNodes.count) + navigation.currentIndex = 0; + navigation.nextItem(); + } + } + + Label { + id: currentSearchLabel + text: " " + (navigation.currentIndex + 1) + "/" + filteredNodes.count + padding: 0 + visible: graphSearchBar.text !== "" + } + + } + + Repeater { + id: filteredNodes + model: SortFilterDelegateModel { + model: root.graph ? root.graph.nodes : undefined + sortRole: "label" + filters: [{role: "label", value: graphSearchBar.text}] + delegate: Item { + property var index_: index + } + function modelData(item, roleName_) { + return item.model.object[roleName_] + } + } + } + + function nextItem() + { + // compute bounding box + var node = nodeRepeater.itemAt(filteredNodes.itemAt(navigation.currentIndex).index_) + var bbox = Qt.rect(node.x, node.y, node.width, node.height) + // rescale to fit the bounding box in the view, zoom is limited to prevent huge text + draggable.scale = Math.min(Math.min(root.width/bbox.width, root.height/bbox.height),maxZoom) + // recenter + draggable.x = bbox.x*draggable.scale*-1 + (root.width-bbox.width*draggable.scale)*0.5 + draggable.y = bbox.y*draggable.scale*-1 + (root.height-bbox.height*draggable.scale)*0.5 + } + } + function registerAttributePin(attribute, pin) { root._attributeToDelegate[attribute] = pin diff --git a/meshroom/ui/qml/GraphEditor/GraphEditorSettings.qml b/meshroom/ui/qml/GraphEditor/GraphEditorSettings.qml index f6d2d35006..188ae81d34 100644 --- a/meshroom/ui/qml/GraphEditor/GraphEditorSettings.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditorSettings.qml @@ -8,5 +8,11 @@ import Qt.labs.settings 1.0 Settings { category: 'GraphEditor' property bool showAdvancedAttributes: false + property bool showDefaultAttributes: true + property bool showModifiedAttributes: true + property bool showInputAttributes: true + property bool showOutputAttributes: true + property bool showLinkAttributes: true + property bool showNotLinkAttributes: true property bool lockOnCompute: true } diff --git a/meshroom/ui/qml/GraphEditor/NodeEditor.qml b/meshroom/ui/qml/GraphEditor/NodeEditor.qml index 92c8ccc152..d5744827c4 100644 --- a/meshroom/ui/qml/GraphEditor/NodeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/NodeEditor.qml @@ -86,6 +86,12 @@ Panel { } } + SearchBar { + id: searchBar + width: 150 + enabled: tabBar.currentIndex === 0 || tabBar.currentIndex === 5 + } + MaterialToolButton { text: MaterialIcons.more_vert font.pointSize: 11 @@ -96,18 +102,79 @@ Panel { 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 + Menu { + id: filterAttributesMenu + title: "Filter Attributes" + RowLayout { + CheckBox { + id: outputToggle + text: "Output" + checkable: true + checked: GraphEditorSettings.showOutputAttributes + onClicked: GraphEditorSettings.showOutputAttributes = !GraphEditorSettings.showOutputAttributes + enabled: tabBar.currentIndex === 0 + } + CheckBox { + id: inputToggle + text: "Input" + checkable: true + checked: GraphEditorSettings.showInputAttributes + onClicked: GraphEditorSettings.showInputAttributes = !GraphEditorSettings.showInputAttributes + enabled: tabBar.currentIndex === 0 + } + } + MenuSeparator {} + RowLayout { + CheckBox { + id: defaultToggle + text: "Default" + checkable: true + checked: GraphEditorSettings.showDefaultAttributes + onClicked: GraphEditorSettings.showDefaultAttributes = !GraphEditorSettings.showDefaultAttributes + enabled: tabBar.currentIndex === 0 + } + CheckBox { + id: modifiedToggle + text: "Modified" + checkable: true + checked: GraphEditorSettings.showModifiedAttributes + onClicked: GraphEditorSettings.showModifiedAttributes = !GraphEditorSettings.showModifiedAttributes + enabled: tabBar.currentIndex === 0 + } + } + MenuSeparator {} + RowLayout { + CheckBox { + id: linkToggle + text: "Link" + checkable: true + checked: GraphEditorSettings.showLinkAttributes + onClicked: GraphEditorSettings.showLinkAttributes = !GraphEditorSettings.showLinkAttributes + enabled: tabBar.currentIndex === 0 + } + CheckBox { + id: notLinkToggle + text: "Not Link" + checkable: true + checked: GraphEditorSettings.showNotLinkAttributes + onClicked: GraphEditorSettings.showNotLinkAttributes = !GraphEditorSettings.showNotLinkAttributes + enabled: tabBar.currentIndex === 0 + } + } + MenuSeparator {} + CheckBox { + id: advancedToggle + text: "Advanced" + 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 } - checkable: true - checked: GraphEditorSettings.showAdvancedAttributes - onClicked: GraphEditorSettings.showAdvancedAttributes = !GraphEditorSettings.showAdvancedAttributes } MenuItem { text: "Open Cache Folder" @@ -186,12 +253,14 @@ Panel { AttributeEditor { id: inOutAttr + objectsHideable: true Layout.fillHeight: true Layout.fillWidth: true model: root.node.attributes readOnly: root.readOnly || root.isCompatibilityNode onAttributeDoubleClicked: root.attributeDoubleClicked(mouse, attribute) onUpgradeRequest: root.upgradeRequest() + filterText: searchBar.text } Loader { @@ -251,12 +320,14 @@ Panel { AttributeEditor { id: nodeInternalAttr + objectsHideable: false Layout.fillHeight: true Layout.fillWidth: true model: root.node.internalAttributes readOnly: root.readOnly || root.isCompatibilityNode onAttributeDoubleClicked: root.attributeDoubleClicked(mouse, attribute) onUpgradeRequest: root.upgradeRequest() + filterText: searchBar.text } } } diff --git a/meshroom/ui/qml/Viewer3D/Inspector3D.qml b/meshroom/ui/qml/Viewer3D/Inspector3D.qml index 60b14c9b48..5b03ad209a 100644 --- a/meshroom/ui/qml/Viewer3D/Inspector3D.qml +++ b/meshroom/ui/qml/Viewer3D/Inspector3D.qml @@ -172,273 +172,290 @@ FloatingPane { checked: true } - ListView { - id: mediaListView + ColumnLayout { anchors.fill: parent - clip: true - model: mediaLibrary.model - spacing: 4 - - ScrollBar.vertical: ScrollBar { id: scrollBar } - - currentIndex: -1 - Connections { - target: uigraph - function onSelectedNodeChanged() { - mediaListView.currentIndex = -1 - } + SearchBar { + id: searchBar + Layout.minimumWidth: 150 + Layout.fillWidth: true + Layout.rightMargin: 10 + Layout.leftMargin: 10 } - Connections { - target: mediaLibrary - function onLoadRequest(idx) { - mediaListView.positionViewAtIndex(idx, ListView.Visible) - } - } + ListView { + id: mediaListView + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + spacing: 4 - 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 ? uigraph.hoveredNode === model.attribute.node : false) : containsMouse - property bool isSelectedNode: model.attribute ? (uigraph ? uigraph.selectedNode === model.attribute.node : false) : false + ScrollBar.vertical: ScrollBar { id: scrollBar } - onIsSelectedNodeChanged: updateCurrentIndex() + currentIndex: -1 - function updateCurrentIndex() { - if(isSelectedNode) { mediaListView.currentIndex = index } - } - - height: childrenRect.height - width: { - if (parent != null) - return parent.width - scrollBar.width - return undefined + Connections { + target: uigraph + function onSelectedNodeChanged() { + mediaListView.currentIndex = -1 + } } - 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); + Connections { + target: mediaLibrary + function onLoadRequest(idx) { + mediaListView.positionViewAtIndex(idx, ListView.Visible) + } } - RowLayout { - width: parent.width - spacing: 2 - - property string src: model.source - onSrcChanged: focusAnim.restart() - - Connections { - target: mediaListView - function onCountChanged() { - mediaDelegate.updateCurrentIndex() + model: SortFilterDelegateModel { + model: mediaLibrary.model + sortRole: "" + filters: [{role: "label", value: searchBar.text}] + 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 ? uigraph.hoveredNode === model.attribute.node : false) : containsMouse + property bool isSelectedNode: model.attribute ? (uigraph ? uigraph.selectedNode === model.attribute.node : false) : false + + onIsSelectedNodeChanged: updateCurrentIndex() + + function updateCurrentIndex() { + if(isSelectedNode) { mediaListView.currentIndex = 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"; + height: childrenRect.height + width: { + if (parent != null) + return parent.width - scrollBar.width + return undefined } - } - // 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 + hoverEnabled: true + onEntered: { if(model.attribute) uigraph.hoveredNode = model.attribute.node } + onExited: { if(model.attribute) uigraph.hoveredNode = null } onClicked: { - if(hoverArea.modifiers & Qt.ControlModifier) - mediaLibrary.solo(index); + if(model.attribute) + uigraph.selectedNode = model.attribute.node; else - model.visible = !model.visible + uigraph.selectedNode = null; + if(mouse.button == Qt.RightButton) + contextMenu.popup(); + mediaListView.currentIndex = 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; - } + onDoubleClicked: { + model.visible = true; + nodeActivated(model.attribute.node); } - } - // BoundingBox visibility (if meshing node) - MaterialToolButton { - visible: model.hasBoundingBox - enabled: model.visible - Layout.alignment: Qt.AlignTop - Layout.fillHeight: true - text: MaterialIcons.transform - font.pointSize: 10 - ToolTip.text: model.displayBoundingBox ? "Hide BBox" : "Show BBox" - flat: true - opacity: model.visible ? (model.displayBoundingBox ? 1.0 : 0.6) : 0.6 - onClicked: { - model.displayBoundingBox = !model.displayBoundingBox - } - } + RowLayout { + width: parent.width + spacing: 2 - // Transform visibility (if SfMTransform node) - MaterialToolButton { - visible: model.hasTransform - enabled: model.visible - Layout.alignment: Qt.AlignTop - Layout.fillHeight: true - text: MaterialIcons._3d_rotation - font.pointSize: 10 - ToolTip.text: model.displayTransform ? "Hide Gizmo" : "Show Gizmo" - flat: true - opacity: model.visible ? (model.displayTransform ? 1.0 : 0.6) : 0.6 - onClicked: { - model.displayTransform = !model.displayTransform - } - } + property string src: model.source + onSrcChanged: focusAnim.restart() - // Media label and info - Item { - implicitHeight: childrenRect.height - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - ColumnLayout { - id: centralLayout - width: parent.width - spacing: 1 + Connections { + target: mediaListView + function onCountChanged() { + mediaDelegate.updateCurrentIndex() + } + } - 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 - function onLoadRequest(idx) { - if(idx === index) - focusAnim.restart() - } - } - ColorAnimation on color { - id: focusAnim - from: label.palette.highlight - to: "transparent" - duration: 2000 + // 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; } } } + + // BoundingBox visibility (if meshing node) + MaterialToolButton { + visible: model.hasBoundingBox + enabled: model.visible + Layout.alignment: Qt.AlignTop + Layout.fillHeight: true + text: MaterialIcons.transform + font.pointSize: 10 + ToolTip.text: model.displayBoundingBox ? "Hide BBox" : "Show BBox" + flat: true + opacity: model.visible ? (model.displayBoundingBox ? 1.0 : 0.6) : 0.6 + onClicked: { + model.displayBoundingBox = !model.displayBoundingBox + } + } + + // Transform visibility (if SfMTransform node) + MaterialToolButton { + visible: model.hasTransform + enabled: model.visible + Layout.alignment: Qt.AlignTop + Layout.fillHeight: true + text: MaterialIcons._3d_rotation + font.pointSize: 10 + ToolTip.text: model.displayTransform ? "Hide Gizmo" : "Show Gizmo" + flat: true + opacity: model.visible ? (model.displayTransform ? 1.0 : 0.6) : 0.6 + onClicked: { + model.displayTransform = !model.displayTransform + } + } + + // Media label and info Item { - visible: infoButton.checked - Layout.fillWidth: true implicitHeight: childrenRect.height - Flow { + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + ColumnLayout { + id: centralLayout 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) } + 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 + function onLoadRequest(idx) { + if(idx === index) + focusAnim.restart() + } + } + ColorAnimation on color { + id: focusAnim + from: label.palette.highlight + to: "transparent" + duration: 2000 + } + } } - RowLayout { - spacing: 1 - visible: model.faceCount - MaterialLabel { text: MaterialIcons.details; rotation: -180 } - Label { text: Format.intToString(model.faceCount) } + 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)) } - RowLayout { - spacing: 1 - visible: model.cameraCount - MaterialLabel { text: MaterialIcons.videocam } - Label { text: model.cameraCount } + MenuItem { + text: "Copy Path" + onTriggered: Clipboard.setText(Filepath.normpath(model.source)) } - RowLayout { - spacing: 1 - visible: model.textureCount - MaterialLabel { text: MaterialIcons.texture } - Label { text: model.textureCount } + 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: !loading && mediaDelegate.containsMouse - text: MaterialIcons.clear - font.pointSize: 10 + visible: !loading && mediaDelegate.containsMouse + text: MaterialIcons.clear + font.pointSize: 10 - ToolTip.text: "Remove" - ToolTip.delay: 500 - onClicked: mediaLibrary.remove(index) - } + 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 + // Media loading indicator + BusyIndicator { + visible: loading + running: visible + padding: removeButton.padding + implicitHeight: implicitWidth + implicitWidth: removeButton.width + } + } } } }