1

Note: the function in QtQuickUtils.js in the following testcase is just to abstract away the boilerplate involved in creating a QML object from a component URL.

Testcase:

main.qml:

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import "QtQuickUtils.js" as QtQuickUtils

Window {
    visible: true
    width: 640
    height: 480

    GridLayout {
        anchors.fill: parent
        id: container
        columns: 1
    }

    Component.onCompleted: {
        QtQuickUtils.createObjectFromComponent("qrc:///MyItem.qml", container, {
            "Layout.fillWidth": true, "Layout.fillHeight": true
            // "width": 100, "height": 100
        });
    }
}

MyItem.qml:

import QtQuick 2.0

Rectangle {
    color: "red"
}

QtQuickUtils.js:

.import QtQml 2.0 as Qml
.pragma library

function createObjectFromComponent(componentUrl, parent, properties) {
    var component = Qt.createComponent(componentUrl);
    function finishCreation() {
        console.log("finishCreation");
        if (component.status === Qml.Component.Ready) {
            var obj = component.createObject(parent, properties);
            if (obj === null) {
                console.log("Error creating object");
                return;
            }
            console.log("success in creating obj");
        } else if (component.status === Qml.Component.Error) {
            console.log("Error loading component:", component.errorString());
            return;
        }
    }
    if (component.status === Qml.Component.Ready) {
        finishCreation();
    } else {
        component.statusChanged.connect(function() { finishCreation(); });
    }
}

This shows nothing (but "finishCreation" and "success in creating obj" are printed).

If I comment out the "Layout.fillWidth": true, "Layout.fillHeight": true line and uncomment the one after that, the item is displayed.

Also if I move the function from the JS file to main.qml, the item is displayed.

I tried moving the function from the JS file into a new qml file (tried both making this QML file singleton and not), but that didn't fix it.

Any idea what I'm doing wrong, and a proper fix?

dtech
  • 47,916
  • 17
  • 112
  • 190
Stefan Monov
  • 11,332
  • 10
  • 63
  • 120
  • `As they are shared, .pragma library files cannot access QML component instance objects or properties directly, although QML values can be passed as function parameters.` – dtech Jan 31 '17 at 16:08
  • @ddriver: I think you have the same confusion as I did in the [this question](http://stackoverflow.com/q/39497187). See the accepted answer there, I think it's highly likely that you'll learn something new there. – Stefan Monov Jan 31 '17 at 16:13
  • I personally don't use `pragma library` instead I use singletons with the functions in there, this allows sharing of the code plus direct support for QML properties with notifications and naturally, to have QML objects rather than just JS objects. – dtech Jan 31 '17 at 16:15
  • Your code works for me if I move the utils file into `main.qml`. Maybe there is a problem with using `.pragma`? – Mitch Jan 31 '17 at 16:33
  • @ddriver: I tried switching from `pragma library` to a QML singleton but that didn't fix the problem. And I don't need to declare any properties in my library, so I'll probably stick to using `pragma library`. – Stefan Monov Jan 31 '17 at 16:33
  • Oh, I just now saw that you already mentioned that. – Mitch Jan 31 '17 at 16:35

1 Answers1

2

The JS file doesn't know what a Layout is, therefore it cannot set it.

// in a JS file, shared or not
function foo(item) { console.log(item.Layout) } // undefined

If you were to:

.import QtQuick.Layouts 1.3 as L // you can't import without "as" in .JS
function foo(item) { console.log(item.L.Layout) } // and it suddenly works

So adding a minor inconvenience, you can achieve the goal by simply using:

"L.Layout.fillWidth": true, "L.Layout.fillHeight": true

However, it will work with a singleton without that extra step, because there you can do an "anonymous" import QtQuick.Layouts 1.3, and suddenly life is easy again. As I mentioned in the comments, I really don't see a reason to use a pragma library, unless maybe you are using some third party JS library. For everything else QML singletons are much more usable, you get the "shared" part, plus support for QML objects, properties with notifications, signals, bindings and all that good stuff.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Also, I find it odd that it doesn't complain about being unable to resolve attached properties, whether it is from a JS or a QML file, it is mute on the subject. Sounds like yet another QML design overlook. – dtech Jan 31 '17 at 18:32
  • Hm, I thought that when looked at from JS, the `Layout` in `foo.Layout.bar` is treated as just a property, and therefore doesn't need to be imported. Why is this not so? – Stefan Monov Feb 01 '17 at 14:59
  • Or, to ask a better defined question: in what cases does the `baz.qux` syntax in JS not mean accessing the property `qux` of the object `baz`? – Stefan Monov Feb 01 '17 at 15:10
  • 1
    I don't know the implementation detail, but you have to have the respective module imported when using the respective attached properties. You have it in the QML but the actual property setting happens inside the JS file. For regular properties it is not an issue but attached properties are different. Another limitation is you cannot lookup dynamic scope properties for an object, like `obj.somePropertyDefinedDownTheTree` - those lookups only work from inside an object's scope, so you need to write an accessor function if you want that. – dtech Feb 01 '17 at 15:19