Z-order in JUCE is weird

JUCE lets you define the z-order of child components as an optional second argument to addAndMakeVisible, which is nice. However, I find the way it works quite odd and not how I think z-order should work (please correct me if I’m wrong).

In particular, if a component has N children, then all z-indices >= N will be considered equivalent, because of this code in Component::addChildComponent:

if (zOrder < 0 || zOrder > childComponentList.size())
    zOrder = childComponentList.size();

The surprising consequence is that if you have three children A, B, C, which you add in this order, with z-index 0, 2, 1, respectively, it means something different than if you use 1, 3, 2 instead. In the former case, B will be the top-most component (as expected). However, in the latter case, surprisingly the top-most component is C, even though B has the higher z-index.

By the same token, negative z-indices are meaningless in JUCE, which I find equally surprising.

I believe that the expected behaviour is that components will always be correctly sorted by their z-indices, with higher indices meaning more in front, and that any integer is a valid z-index.

Dear JUCE team, could you please fix this? Or alternatively, tell me why the current behaviour is the expected one?

Many thanks :heart:

P.S. @jules, I see what you did there, but… what do you think about this: not to use the zIndex to index into juce::Component::childComponentList, but instead store it explicitly alongside the child component, and use its values (which can then be arbitrary) to maintain the order of the array? Basically, make childComponentList something similar to a flat_map with zOrder as the key?

6 Likes

I think the way juce does it is fairly standard, coming from either a Cocoa or Win32 background. The z-order is the index of the component in the array of child components. Setting a z-order of 3
doesn’t make any sense unless you have a total of 4 child components, therefore it’s bounded by the number of indexes in the array.

The issue with using a flat_map is you can only have one object at each key, which makes moving components to the front of the z-order an expensive operation (everything needs to be re-indexed). Also inserting becomes expensive since everything would need to be re-indexed.

2 Likes

Yeah - the reasons for using a plain array are roughly:

  • The memory footprint of the Component class is important, and the number of elements is usually very small, so we want a data structure that’s as tight as possible for that
  • Most of the code that iterates the children expects to do so in order of their z-order - numerous algorithms rely on this, and changing it would break existing code

TBH if I was going to change this at all, I’d probably just remove the use of integers for z-order and instead have options to insert them in front of or behind other components

Fair enough. I can live with the status quo. Thank you very much for your comments.