The Mystery and Voodoo of Attached Properties
-
Hi Everyone,
I'm trying to work out whether Attached Properties is something I should be using in my current project. This could be a longish post, so please bare with me =)
I'll start off by explaining my goal. And then the source of my confusion =p
The goal: I have a root level object, which has a shape container. The shape container... well, contains shapes. Each shape has several "Handles". The root object has a property which keeps track of a single Handle.
Super simplified code/architecture:
main.qml @
Editor {
id: editor
currentHandle: nullShapeContainer{ shapes: [ /* Insert list of shapes here */ ] }
}
@MyShape.qml
@
Shape {
Handle{}
Handle{}
}
@Handle.qml
@
HandleCpp {
id: handle
MouseArea {
// Insert MouseArea house-keeping here/****** THIS IS THE IMPORTANT PART ******/ onEntered: editor.currentHandle = handle onExited: editor.currentHandle = null }
}
@Okay, so I think that attached properties are probably a useful thing to have here. And change Handle.qml to do the following instead:
@onEntered: Editor.currentHandle = handle@One of my motivations for doing this is to allow only Handle items to modify this property by implementing it smartly in C++. Which leads me to...
The source of my confusion: The docs "here":http://qt-project.org/doc/qt-5.0/qtqml/qtqml-syntax-objectattributes.html#attached-properties-and-attached-signal-handlers explain that attached properties DO NOT simply get passed to children items, but have to be attached specifically. And then the example "here":http://qt-project.org/doc/qt-4.8/declarative-cppextensions-referenceexamples-attached.html seems to show that the Person class gets the attached property from BirthdayParty implicitly.
In conclusion:
Is there a way for me to manually select/limit which components have access to an attached property?
How can that attached property link back to the attacher (Editor in this case)? (Similar to ListView.currentItem)
-
Attached properties do not get passed to children items; those docs are correct. In the BirthdayParty example, the Person class does no get an attached property from BirthdayParty implicitly; it only gets an attached property because the code specifically attaches a BirthdayParty.rsvp property to it.
In short, attached properties can be attached to any object that you like. There are absolutely no restrictions, because the concept simply involves attaching some arbitrary property (like BirthdayParty.rsvp or ListView.isCurrentItem) to some arbitrary object. For example, ListView has an attached property called "delayRemove". If you wanted to, you could attach this property to a MouseArea like this:
@
MouseArea {
ListView.delayRemove: true
}
@and this would work -- there would be no syntax errors, because the attached properties (and signal handlers) can be attached to any object you like. However, this example would get no absolutely no benefit, because nobody would bother to check the value of "ListView.delayRemove" for a MouseArea. On the other hand, ListView internally checks whether its delegates have set ListView.delayRemove so that it can respond appropriately.
bq. Is there a way for me to manually select/limit which components have access to an attached property?
Since an attached property can be attached to any object, as explaine above, you can't prevent the actual QML code that attaches a property. However I suppose you could limit the response in the backend.
When an attached property is attached to an object, qmlAttachedProperties(QObject*) is called and the object is passed to that method. For example in the BirthdayPart code, BirthdayParty::qmlAttachedProperties(QObject*) is called and the object passed will be a Boy* or Girl* since example.qml attaches BirthdayParty.rsvp to Boy and Girl objects. I guess you could check the type of the object in that method and print some warning if you like and avoid setting the property values if the object is not of the desired type, if the property change may be expensive.
bq. How can that attached property link back to the attacher (Editor in this case)? (Similar to ListView.currentItem)
The Editor would need access to the created instances of HandleCpp somehow so it can set an appropriate property e.g. Editor.isCurrentHandle. For example, ListView internally attaches the ListView.isCurrentItem property to all of the delegates it creates so that they have access to this property. You would need to do some similar set-up for created instances of HandleCpp, to allow something like:
@
HandleCpp {
id: handle
MouseArea {
onClicked: console.log(handle.Editor.isCurrentHandle)
}
}
@Notice the reference is "handle.Editor.isCurrentHandle" rather than just "Editor.isCurrentHandle". I suggest you attach the Editor.isCurrentHandle property (or whatever) to the HandleCpp rather than the MouseArea, as in your example.
-
Ah, didn't realise it was you, Kit :) Feel free to ping on #qt-qml if you have more questions. I'm sure a doc patch would be appreciated. The current docs as far as I know are "here":http://qt-project.org/doc/qt-5.0/qtqml/qtqml-syntax-objectattributes.html#attached-properties-and-attached-signal-handlers (for using attached properties/handlers in general) and "here":http://qt-project.org/doc/qt-5.0/qtqml/qtqml-cppintegration-definetypes.html#providing-attached-objects-for-data-annotations (for how to implement them in C++).