[qt6][Viewer3D] MediaLibrary: Fix loading medias from attributes

Additionally harmonize comments and explicitly declare parameters
for slots that need them.
This commit is contained in:
Candice Bentéjac 2024-10-01 16:42:18 +02:00
parent 2a7844480b
commit e1ef8acadc

View file

@ -93,17 +93,18 @@ Entity {
console.warn("Media Error: File " + pathStr + " does not exist.")
return
}
// file already loaded, return
// File already loaded, return
if (m.sourceToEntity[pathStr]) {
ensureVisible(pathStr)
return
}
// add file to the internal ListModel
m.mediaModel.append(makeElement({
"source": pathStr,
"label": label ? label : Filepath.basename(pathStr),
"section": "External"
// Add file to the internal ListModel
m.mediaModel.append(
makeElement({
"source": pathStr,
"label": label ? label : Filepath.basename(pathStr),
"section": "External"
}))
}
@ -115,16 +116,17 @@ Entity {
var attrLabel = attribute.isOutput ? "" : attribute.fullName.replace(attribute.node.name, "")
var section = attribute.node.label
// add file to the internal ListModel
m.mediaModel.append(makeElement({
// Add file to the internal ListModel
m.mediaModel.append(
makeElement({
"label": section + attrLabel,
"section": section,
"attribute": attribute,
"attribute": attribute
}))
}
function remove(index) {
// remove corresponding entry from model
// Remove corresponding entry from model
m.mediaModel.remove(index)
}
@ -189,7 +191,7 @@ Entity {
MediaLoader {
id: mediaLoader
// whether MediaLoader has been fully instantiated by the NodeInstantiator
// Whether MediaLoader has been fully instantiated by the NodeInstantiator
property bool fullyInstantiated: false
// explicitly store some attached model properties for outside use and ease binding
@ -198,23 +200,23 @@ Entity {
readonly property var modelSource: attribute || model.source
readonly property bool visible: model.visible
// multi-step binding to ensure MediaLoader source is properly
// Multi-step binding to ensure MediaLoader source is properly
// updated when needed, whether raw source is valid or not
// raw source path
// Raw source path
property string rawSource: attribute ? attribute.value : model.source
// whether dependencies are statified (applies for output/connected input attributes only)
// Whether dependencies are statified (applies for output/connected input attributes only)
readonly property bool dependencyReady: {
if (attribute) {
const rootAttribute = attribute.isLink ? attribute.rootLinkParam : attribute
if (rootAttribute.isOutput)
return rootAttribute.node.globalStatus === "SUCCESS"
}
return true // is an input param without link (so no dependency) or an external file
return true // Is an input param without link (so no dependency) or an external file
}
// source based on raw source + dependency status
// Source based on raw source + dependency status
property string currentSource: dependencyReady ? rawSource : ""
// source based on currentSource + "requested" property
// Source based on currentSource + "requested" property
property string finalSource: model.requested ? currentSource : ""
// To use only if we want to draw the input source and not the current node output (Warning: to use with caution)
@ -229,14 +231,6 @@ Entity {
renderMode: root.renderMode
enabled: visible
// QObject.destroyed signal is not accessible
// Use the object as NodeInstantiator model to be notified of its deletion
NodeInstantiator {
model: attribute
delegate: Entity { objectName: "DestructionWatcher [" + model.toString() + "]" }
onObjectRemoved: remove(index)
}
property bool alive: attribute ? attribute.node.alive : false
onAliveChanged: {
if (!alive && index >= 0)
@ -245,19 +239,19 @@ Entity {
// 'visible' property drives media loading request
onVisibleChanged: {
// always request media loading if visible
// Always request media loading if visible
if (model.visible)
model.requested = true
// only cancel loading request if media is not valid
// Only cancel loading request if media is not valid
// (a media won't be unloaded if already loaded, only hidden)
else if (!model.valid)
model.requested = false
}
function updateCacheAndModel(forceRequest) {
// don't cache explicitly unloaded media
// Don't cache explicitly unloaded media
if (model.requested && object && dependencyReady) {
// cache current object
// Cache current object
if (cache.add(Filepath.urlToString(mediaLoader.source), object))
object = null
}
@ -265,21 +259,21 @@ Entity {
}
function updateModel(forceRequest) {
// update model's source path if input is an attribute
// Update model's source path if input is an attribute
if (attribute) {
model.source = rawSource
}
// auto-restore entity if raw source is in cache
// Auto-restore entity if raw source is in cache
model.requested = forceRequest || (!model.valid && model.requested) || cache.contains(rawSource)
model.valid = Filepath.exists(rawSource) && dependencyReady
}
Component.onCompleted: {
// keep 'source' -> 'entity' reference
// Keep 'source' -> 'entity' reference
m.sourceToEntity[modelSource] = instantiatedEntity
// always request media loading when delegate has been created
// Always request media loading when delegate has been created
updateModel(true)
// if external media failed to open, remove element from model
// If external media failed to open, remove element from model
if (!attribute && !object)
remove(index)
}
@ -293,7 +287,7 @@ Entity {
}
onFinalSourceChanged: {
// update media visibility
// Update media visibility
// (useful if media was explicitly unloaded or hidden but loaded back from cache)
model.visible = model.requested
@ -301,7 +295,7 @@ Entity {
cached = cachedObject !== undefined
if (cached) {
object = cachedObject
// only change cached object parent if mediaLoader has been fully instantiated
// Only change cached object parent if mediaLoader has been fully instantiated
// by the NodeInstantiator; otherwise re-parenting will fail silently and the object will disappear...
// see "onFullyInstantiatedChanged" and parent NodeInstantiator's "onObjectAdded"
if (fullyInstantiated) {
@ -310,28 +304,28 @@ Entity {
}
mediaLoader.source = Filepath.stringToUrl(finalSource)
if (object) {
// bind media info to corresponding model roles
// Bind media info to corresponding model roles
// (test for object validity to avoid error messages right after object has been deleted)
var boundProperties = ["vertexCount", "faceCount", "cameraCount", "textureCount", "resectionIdCount", "resectionId", "resectionGroups"]
boundProperties.forEach(function(prop) {
model[prop] = Qt.binding(function() { return object ? object[prop] : 0 })
})
} else if (finalSource && status === Component.Ready) {
// source was valid but no loader was created, remove element
// check if component is ready to avoid removing element from the model before adding instance to the node
// Source was valid but no loader was created, remove element
// Check if component is ready to avoid removing element from the model before adding instance to the node
remove(index)
}
}
onFullyInstantiatedChanged: {
// delayed reparenting of object coming from the cache
// Delayed reparenting of object coming from the cache
if (object)
object.parent = mediaLoader
}
onStatusChanged: {
model.status = status
// remove model entry for external media that failed to load
// Remove model entry for external media that failed to load
if (status === SceneLoader.Error && !model.attribute)
remove(index)
}
@ -340,7 +334,7 @@ Entity {
ObjectPicker {
enabled: mediaLoader.enabled && pickingEnabled
hoverEnabled: false
onPressed: root.pressed(pick)
onPressed: function(pick) { root.pressed(pick) }
}
]
}
@ -384,12 +378,12 @@ Entity {
}
}
onObjectAdded: {
// notify object that it is now fully instantiated
onObjectAdded: function(index, object) {
// Notify object that it is now fully instantiated
object.fullyInstantiated = true
}
onObjectRemoved: {
onObjectRemoved: function(index, object) {
if (m.sourceToEntity[object.modelSource])
delete m.sourceToEntity[object.modelSource]
}