[ui] improve atts filtering and add search bar ...

...for node attributes and 3D inspector

- add search bar for node attributes
- add search bar for 3D inspector
- improve attributes filtering by adding more flexibility
and fixing some issues
This commit is contained in:
Abdelrahman AL MAROUK 2023-07-03 09:25:17 +02:00
parent 6c2e9eb583
commit b5093ac3a0
6 changed files with 408 additions and 301 deletions

View file

@ -111,6 +111,10 @@ class Attribute(BaseObject):
def getLabel(self): def getLabel(self):
return self._label return self._label
@Slot(str, result=bool)
def matchText(self, text):
return self.fullLabel.lower().find(text.lower()) > -1
def getFullLabel(self): def getFullLabel(self):
""" Full Label includes the name of all parent groups, e.g. 'groupLabel subGroupLabel Label' """ """ Full Label includes the name of all parent groups, e.g. 'groupLabel subGroupLabel Label' """
@ -349,6 +353,7 @@ class Attribute(BaseObject):
isOutput = Property(bool, isOutput.fget, constant=True) isOutput = Property(bool, isOutput.fget, constant=True)
isLinkChanged = Signal() isLinkChanged = Signal()
isLink = Property(bool, isLink.fget, notify=isLinkChanged) isLink = Property(bool, isLink.fget, notify=isLinkChanged)
isLinkNested = isLink
hasOutputConnectionsChanged = Signal() hasOutputConnectionsChanged = Signal()
hasOutputConnections = Property(bool, hasOutputConnections.fget, notify=hasOutputConnectionsChanged) hasOutputConnections = Property(bool, hasOutputConnections.fget, notify=hasOutputConnectionsChanged)
isDefault = Property(bool, _isDefault, notify=valueChanged) isDefault = Property(bool, _isDefault, notify=valueChanged)
@ -502,10 +507,19 @@ class ListAttribute(Attribute):
for attr in self._value: for attr in self._value:
attr.updateInternals() 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 # Override value property setter
value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged) value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged)
isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged) isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged)
baseType = Property(str, getBaseType, constant=True) baseType = Property(str, getBaseType, constant=True)
isLinkNested = Property(bool, isLinkNested.fget)
class GroupAttribute(Attribute): class GroupAttribute(Attribute):
@ -622,6 +636,10 @@ class GroupAttribute(Attribute):
for attr in self._value: for attr in self._value:
attr.updateInternals() 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 # Override value property
value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged) value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged)
isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged) isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged)

View file

@ -13,6 +13,7 @@ ListView {
property bool readOnly: false property bool readOnly: false
property int labelWidth: 180 property int labelWidth: 180
property bool objectsHideable: true property bool objectsHideable: true
property string filterText: ""
signal upgradeRequest() signal upgradeRequest()
signal attributeDoubleClicked(var mouse, var attribute) signal attributeDoubleClicked(var mouse, var attribute)
@ -24,21 +25,21 @@ ListView {
ScrollBar.vertical: ScrollBar { id: scrollBar } ScrollBar.vertical: ScrollBar { id: scrollBar }
delegate: Loader { delegate: Loader {
active: { active: object.enabled && (
object.enabled !objectsHideable
&& (!object.desc.advanced || GraphEditorSettings.showAdvancedAttributes) || ((!object.desc.advanced || GraphEditorSettings.showAdvancedAttributes)
&& ( && (object.isDefault && GraphEditorSettings.showDefaultAttributes || !object.isDefault && GraphEditorSettings.showModifiedAttributes)
!(object.isDefault || object.isOutput || object.isLink) && (object.isOutput && GraphEditorSettings.showOutputAttributes || !object.isOutput && GraphEditorSettings.showInputAttributes)
|| !GraphEditorSettings.showOnlyModifiedAttributes && (object.isLinkNested && GraphEditorSettings.showLinkAttributes || !object.isLink && GraphEditorSettings.showNotLinkAttributes))
|| !objectsHideable ) && object.matchText(filterText)
)
}
visible: active visible: active
sourceComponent: AttributeItemDelegate { sourceComponent: AttributeItemDelegate {
width: root.width - scrollBar.width width: root.width - scrollBar.width
readOnly: root.readOnly readOnly: root.readOnly
labelWidth: root.labelWidth labelWidth: root.labelWidth
filterText: root.filterText
objectsHideable: root.objectsHideable
attribute: object attribute: object
onDoubleClicked: root.attributeDoubleClicked(mouse, attr) onDoubleClicked: root.attributeDoubleClicked(mouse, attr)
} }

View file

@ -13,6 +13,8 @@ RowLayout {
property variant attribute: null property variant attribute: null
property bool readOnly: false // whether the attribute's value can be modified 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 alias label: parameterLabel // accessor to the internal Label (attribute's name)
property int labelWidth // shortcut to set the fixed size of the Label property int labelWidth // shortcut to set the fixed size of the Label
@ -489,32 +491,39 @@ RowLayout {
ScrollBar.vertical: ScrollBar { id: sb } ScrollBar.vertical: ScrollBar { id: sb }
delegate: RowLayout { delegate: Loader{
id: item active: !objectsHideable
property var childAttrib: object || ((object.isDefault && GraphEditorSettings.showDefaultAttributes || !object.isDefault && GraphEditorSettings.showModifiedAttributes)
layoutDirection: Qt.RightToLeft && (object.isLinkNested && GraphEditorSettings.showLinkAttributes || !object.isLinkNested && GraphEditorSettings.showNotLinkAttributes))
width: lv.width - sb.width visible: active
Component.onCompleted: { height: item ? implicitHeight : -spacing // compensate for spacing if item is hidden
var cpt = Qt.createComponent("AttributeItemDelegate.qml") sourceComponent: RowLayout {
var obj = cpt.createObject(item, id: item
{'attribute': Qt.binding(function() { return item.childAttrib }), property var childAttrib: object
'readOnly': Qt.binding(function() { return !root.editable }) layoutDirection: Qt.RightToLeft
}) width: lv.width - sb.width
obj.Layout.fillWidth = true Component.onCompleted: {
obj.label.text = index var cpt = Qt.createComponent("AttributeItemDelegate.qml")
obj.label.horizontalAlignment = Text.AlignHCenter var obj = cpt.createObject(item,
obj.label.verticalAlignment = Text.AlignVCenter {'attribute': Qt.binding(function() { return item.childAttrib }),
obj.doubleClicked.connect(function(attr) {root.doubleClicked(attr)}) 'readOnly': Qt.binding(function() { return !root.editable })
} })
ToolButton { obj.Layout.fillWidth = true
enabled: root.editable obj.label.text = index
text: MaterialIcons.remove_circle_outline obj.label.horizontalAlignment = Text.AlignHCenter
font.family: MaterialIcons.fontFamily obj.label.verticalAlignment = Text.AlignVCenter
font.pointSize: 11 obj.doubleClicked.connect(function(attr) {root.doubleClicked(attr)})
padding: 2 }
ToolTip.text: "Remove Element" ToolButton {
ToolTip.visible: hovered enabled: root.editable
onClicked: _reconstruction.removeAttribute(item.childAttrib) 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 }), {'model': Qt.binding(function() { return attribute.value }),
'readOnly': Qt.binding(function() { return root.readOnly }), 'readOnly': Qt.binding(function() { return root.readOnly }),
'labelWidth': 100, // reduce label width for children (space gain) '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.Layout.fillWidth = true;
obj.attributeDoubleClicked.connect(function(attr) {root.doubleClicked(attr)}) obj.attributeDoubleClicked.connect(function(attr) {root.doubleClicked(attr)})

View file

@ -8,6 +8,11 @@ import Qt.labs.settings 1.0
Settings { Settings {
category: 'GraphEditor' category: 'GraphEditor'
property bool showAdvancedAttributes: false property bool showAdvancedAttributes: false
property bool showOnlyModifiedAttributes: 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 property bool lockOnCompute: true
} }

View file

@ -86,6 +86,12 @@ Panel {
} }
} }
SearchBar {
id: searchBar
width: 150
enabled: tabBar.currentIndex === 0 || tabBar.currentIndex === 5
}
MaterialToolButton { MaterialToolButton {
text: MaterialIcons.more_vert text: MaterialIcons.more_vert
font.pointSize: 11 font.pointSize: 11
@ -96,32 +102,79 @@ Panel {
Menu { Menu {
id: settingsMenu id: settingsMenu
y: parent.height y: parent.height
MenuItem { Menu {
id: advancedToggle id: filterAttributesMenu
text: "Advanced Attributes" title: "Filter Attributes"
MaterialLabel { RowLayout {
anchors.right: parent.right; anchors.rightMargin: parent.padding; CheckBox {
text: MaterialIcons.build id: outputToggle
anchors.verticalCenter: parent.verticalCenter text: "Output"
font.pointSize: 8 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
}
} }
checkable: true MenuSeparator {}
checked: GraphEditorSettings.showAdvancedAttributes RowLayout {
onClicked: GraphEditorSettings.showAdvancedAttributes = !GraphEditorSettings.showAdvancedAttributes CheckBox {
} id: defaultToggle
MenuItem { text: "Default"
id: modifiedToggle checkable: true
text: "Only Modified Attributes" checked: GraphEditorSettings.showDefaultAttributes
MaterialLabel { onClicked: GraphEditorSettings.showDefaultAttributes = !GraphEditorSettings.showDefaultAttributes
anchors.right: parent.right; anchors.rightMargin: parent.padding; enabled: tabBar.currentIndex === 0
text: MaterialIcons.edit }
anchors.verticalCenter: parent.verticalCenter CheckBox {
font.pointSize: 8 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.showOnlyModifiedAttributes
onClicked: GraphEditorSettings.showOnlyModifiedAttributes = !GraphEditorSettings.showOnlyModifiedAttributes
enabled: tabBar.currentIndex === 0
} }
MenuItem { MenuItem {
text: "Open Cache Folder" text: "Open Cache Folder"
@ -207,6 +260,7 @@ Panel {
readOnly: root.readOnly || root.isCompatibilityNode readOnly: root.readOnly || root.isCompatibilityNode
onAttributeDoubleClicked: root.attributeDoubleClicked(mouse, attribute) onAttributeDoubleClicked: root.attributeDoubleClicked(mouse, attribute)
onUpgradeRequest: root.upgradeRequest() onUpgradeRequest: root.upgradeRequest()
filterText: searchBar.text
} }
Loader { Loader {
@ -273,6 +327,7 @@ Panel {
readOnly: root.readOnly || root.isCompatibilityNode readOnly: root.readOnly || root.isCompatibilityNode
onAttributeDoubleClicked: root.attributeDoubleClicked(mouse, attribute) onAttributeDoubleClicked: root.attributeDoubleClicked(mouse, attribute)
onUpgradeRequest: root.upgradeRequest() onUpgradeRequest: root.upgradeRequest()
filterText: searchBar.text
} }
} }
} }

View file

@ -172,273 +172,290 @@ FloatingPane {
checked: true checked: true
} }
ListView { ColumnLayout {
id: mediaListView
anchors.fill: parent anchors.fill: parent
clip: true
model: mediaLibrary.model
spacing: 4
ScrollBar.vertical: ScrollBar { id: scrollBar } SearchBar {
id: searchBar
currentIndex: -1 Layout.minimumWidth: 150
Layout.fillWidth: true
Connections { Layout.rightMargin: 10
target: uigraph Layout.leftMargin: 10
function onSelectedNodeChanged() {
mediaListView.currentIndex = -1
}
} }
Connections { ListView {
target: mediaLibrary id: mediaListView
function onLoadRequest(idx) { Layout.fillHeight: true
mediaListView.positionViewAtIndex(idx, ListView.Visible) Layout.fillWidth: true
} clip: true
} spacing: 4
delegate: MouseArea { ScrollBar.vertical: ScrollBar { id: scrollBar }
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() currentIndex: -1
function updateCurrentIndex() { Connections {
if(isSelectedNode) { mediaListView.currentIndex = index } target: uigraph
} function onSelectedNodeChanged() {
mediaListView.currentIndex = -1
height: childrenRect.height
width: {
if (parent != null)
return parent.width - scrollBar.width
return undefined
}
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
function onCountChanged() {
mediaDelegate.updateCurrentIndex()
}
} }
}
// Current/selected element indicator Connections {
Rectangle { target: mediaLibrary
Layout.fillHeight: true function onLoadRequest(idx) {
width: 2 mediaListView.positionViewAtIndex(idx, ListView.Visible)
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 model: SortFilterDelegateModel {
MaterialToolButton { model: mediaLibrary.model
Layout.alignment: Qt.AlignTop sortRole: "label"
Layout.fillHeight: true filters: [{role: "label", value: searchBar.text}]
text: model.visible ? MaterialIcons.visibility : MaterialIcons.visibility_off delegate: MouseArea {
font.pointSize: 10 id: mediaDelegate
ToolTip.text: model.visible ? "Hide" : model.requested ? "Show" : model.valid ? "Load and Show" : "Load and Show when Available" // add mediaLibrary.count in the binding to ensure 'entity'
flat: true // is re-evaluated when mediaLibrary delegates are modified
opacity: model.visible ? 1.0 : 0.6 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 }
}
height: childrenRect.height
width: {
if (parent != null)
return parent.width - scrollBar.width
return undefined
}
hoverEnabled: true
onEntered: { if(model.attribute) uigraph.hoveredNode = model.attribute.node }
onExited: { if(model.attribute) uigraph.hoveredNode = null }
onClicked: { onClicked: {
if(hoverArea.modifiers & Qt.ControlModifier) if(model.attribute)
mediaLibrary.solo(index); uigraph.selectedNode = model.attribute.node;
else else
model.visible = !model.visible uigraph.selectedNode = null;
if(mouse.button == Qt.RightButton)
contextMenu.popup();
mediaListView.currentIndex = index;
} }
// Handle modifiers on button click onDoubleClicked: {
MouseArea { model.visible = true;
id: hoverArea nodeActivated(model.attribute.node);
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) RowLayout {
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 {
implicitHeight: childrenRect.height
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
ColumnLayout {
id: centralLayout
width: parent.width width: parent.width
spacing: 1 spacing: 2
Label { property string src: model.source
id: label onSrcChanged: focusAnim.restart()
Connections {
target: mediaListView
function 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;
}
}
}
// 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 {
implicitHeight: childrenRect.height
Layout.fillWidth: true Layout.fillWidth: true
leftPadding: 0 Layout.alignment: Qt.AlignTop
rightPadding: 0 ColumnLayout {
topPadding: 3 id: centralLayout
bottomPadding: topPadding width: parent.width
text: model.label spacing: 1
opacity: model.valid ? 1.0 : 0.6
elide: Text.ElideMiddle Label {
font.weight: mediaListView.currentIndex === index ? Font.DemiBold : Font.Normal id: label
background: Rectangle { Layout.fillWidth: true
Connections { leftPadding: 0
target: mediaLibrary rightPadding: 0
function onLoadRequest(idx) { topPadding: 3
if(idx === index) bottomPadding: topPadding
focusAnim.restart() 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
}
} }
} }
ColorAnimation on color { Item {
id: focusAnim visible: infoButton.checked
from: label.palette.highlight Layout.fillWidth: true
to: "transparent" implicitHeight: childrenRect.height
duration: 2000 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
} }
} }
} }
Item {
visible: infoButton.checked // Remove media from library button
Layout.fillWidth: true MaterialToolButton {
implicitHeight: childrenRect.height id: removeButton
Flow { Layout.alignment: Qt.AlignTop
width: parent.width Layout.fillHeight: true
spacing: 4
visible: model.status === SceneLoader.Ready visible: !loading && mediaDelegate.containsMouse
RowLayout { text: MaterialIcons.clear
spacing: 1 font.pointSize: 10
visible: model.vertexCount
MaterialLabel { text: MaterialIcons.grain } ToolTip.text: "Remove"
Label { text: Format.intToString(model.vertexCount) } ToolTip.delay: 500
} onClicked: mediaLibrary.remove(index)
RowLayout { }
spacing: 1
visible: model.faceCount // Media loading indicator
MaterialLabel { text: MaterialIcons.details; rotation: -180 } BusyIndicator {
Label { text: Format.intToString(model.faceCount) } visible: loading
} running: visible
RowLayout { padding: removeButton.padding
spacing: 1 implicitHeight: implicitWidth
visible: model.cameraCount implicitWidth: removeButton.width
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
} }
} }
} }