[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

@ -112,6 +112,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' """
if isinstance(self.root, ListAttribute): if isinstance(self.root, ListAttribute):
@ -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
} }
} }
} }