[Solved] QML Draw order
-
How does QML draw order work, especially given that QML is declarative rather than imperative? If I have this code:
@
import QtQuick 1.1Rectangle {
id: outerRect
color: "red"
Text {
anchors.centerIn: parent
text: "outerRect"
}
x: 0; y: 0; width: 250; height: 150Rectangle { id: midLevelRect1 color: "blue" Text { anchors.centerIn: parent text: "midLevelRect1" } x: 10; y: 10; width: 75; height: 75 Rectangle { id: innerRect color: "green" Text { anchors.centerIn: parent text: "innerRect" } x: 50; y: 50; width: 75; height: 75 } } Rectangle { id: midLevelRect2 color: "yellow" Text { anchors.centerIn: parent text: "midLevelRect2" } x: 100; y: 10; width: 75; height: 75 }
}
@
I kind of expected (or hoped) to see this:
!http://i.imgur.com/GVEl0Lz.png(Green is over - like a tooltip)!
But what I actually see is this:
!http://i.imgur.com/g2ctf6R.png(Green is under - like... ?)!
Its occurred to me that in a way, declarative syntax doesn't provide a really great way to structure draw order. Most functional languages don't necessarily require a particular applicative order and draw order here is almost sort of a side-effect of the applicative ordering that the QML runtime has been designed around. How does QML do this? It's pretty clear that behind the scenes the qml runtime is evaluating the object tree in a simple and strict dfs order - is this necessarily true all the time?
More to the point, I'm trying to implement something like tooltips. With those screen shots you can imagine the green rectangle being a tooltip and the blue and yellow rectangles are widgets/QWidgets/QML Items. To get the tooltip to display on top of everything, including adjacent widgets, I ended up having to declare it under the common root - in this case I had to declare innerRect underneath outerRect rather than midLevelRect1, even though the tool tip really sort of belongs to midLevelRect1. Where this really causes problems is with view delegates in the qml data model - like having a tooltip for an item in a ListView.
I would have thought that setting innerRect's z property to some large number would draw it over midLevelRect2, but it doesn't. If this isn't what z is for, why do we even have it in Item?
I'm using QtQuick 1.0 in Qt 4.8.4 on Windows, but 4.8.4 on OSX behaves identically. Can anyone fill me in?
-
I think you've misunderstood, or perhaps I haven't explained this well enough. The green rectangle is a tool tip for the blue widget, not the yellow widget. Why would I parent it under the yellow widget if it's the blue widget's tooltip? That would be even crazier than having to parent it under the common parent (the red background rectangle), which is what I ended up having to do.
Having to hack the tooltip under the common parent to get draw order right is ugly because it works against code re-use and encapsulation. Because of this issue, as far as the knowledge that I have right now goes, I wouldn't be able to make a reusable widget that draws its own tooltips.
-
Drawing order in QML is from the first declared element in the QML to last. Every following item will be on top of that Item. Same goes for items within items. This is not so strange, looking for instance to Photoshop and layers the same order applies.
You can use the z: property to 'force' it to be rendered in another order. Default z=0, making the 'tooltip' item for instance z: 100 will for sure draw it on top.
The reason it does not happen in your case is that the green rectangle is a child of the blue rectangle. The z property applies to the drawing order of siblings. So changing the z of the green rectangle will move it on top of other child elements of the blue rectangle, but there aren't any.
Moving the Green rectangle up one level giving it the same parent as the other rectangles, but keeping the same element order, the z property can do its magic.
@
import QtQuick 1.1Rectangle {
id: outerRect
color: "red"
Text {
anchors.centerIn: parent
text: "outerRect"
}
x: 0; y: 0; width: 250; height: 150Rectangle { id: midLevelRect1 color: "blue" Text { anchors.centerIn: parent text: "midLevelRect1" } x: 10; y: 10; width: 75; height: 75 } Rectangle { id: innerRect color: "green" Text { anchors.centerIn: parent text: "innerRect" } x: 50; y: 50; width: 75; height: 75; z: 100 } Rectangle { id: midLevelRect2 color: "yellow" Text { anchors.centerIn: parent text: "midLevelRect2" } x: 100; y: 10; width: 75; height: 75 }
}
@ -
[quote author="JapieKrekel" date="1370178928"]Drawing order in QML is from the first declared element in the QML to last. Every following item will be on top of that Item. Same goes for items within items. This is not so strange, looking for instance to Photoshop and layers the same order applies.
[/quote]Ok - that makes sense.
[quote author="JapieKrekel" date="1370178928"]
You can use the z: property to 'force' it to be rendered in another order. Default z=0, making the 'tooltip' item for instance z: 100 will for sure draw it on top.The reason it does not happen in your case is that the green rectangle is a child of the blue rectangle. The z property applies to the drawing order of siblings.
[/quote]Now this is what's really strange in my opinion. It almost seems like this property should be called 'siblingZ' or 'localZ', not simply 'z'. When I see that 'z' parameter, I'm thinking that this controls more than a sibling-draw-order hack - that it represents a global z draw order. If they're not going to give us a scene-global draw sort with the 'z' property, it would just be better not to call it 'z' at all. Everyone here at my work that's using QML who has tried to use 'z' to change the draw order has been surprised. With the name 'z', it really violates the 'principle of least astonishment'
[quote author="JapieKrekel" date="1370178928"]
So changing the z of the green rectangle will move it on top of other child elements of the blue rectangle, but there aren't any.Moving the Green rectangle up one level giving it the same parent as the other rectangles, but keeping the same element order, the z property can do its magic.
[/quote]Thanks for the explanation - the machinery at work is a bit easier to understand now.
-
Well see z as 'bring-to-front' as in photoshop. Excact same behavior, an object from a layer that is below another layer, will never go totally in front, ony within that layer.
Glad to help, if the topic is solved, please mark it as such.
-
[quote author="tedmiddleton" date="1370140219"]
Having to hack the tooltip under the common parent to get draw order right is ugly because it works against code re-use and encapsulation. Because of this issue, as far as the knowledge that I have right now goes, I wouldn't be able to make a reusable widget that draws its own tooltips.
[/quote]I have the same problem as you, and I was looking for a solution without breaking the encapsulation rules, if you got any please let me know.
Thanks