Strange GuiControlListener's behavior

I’m trying to have a container located at the top-right corner of another panel. For it, I add a GuiControlListener to that panel and listen at it reshape method. It would work if it wasn’t because of the panel has got insets and the reshape method’s size parameter is the panel size before applying the insets. This is something obvious and seems intended if we take a look at the source:

public void setSize( Vector3f size ) {
        if( size.x < 0 || size.y < 0 || size.z < 0 ) {
            throw new IllegalArgumentException("Size cannot be negative:" + size);
        }            
        lastSize.set(size);
        
        // The components will take their parts out of size.
        // The caller may not be expecting their size to change... especially
        // since it might have been the getPreferredSize() of some other GUI element
        Vector3f stackSize = size.clone();
        
        Vector3f offset = new Vector3f();
        for( GuiComponent c : componentStack.getArray() ) {
            c.reshape(offset, stackSize);
            stackSize.x = Math.max(0, stackSize.x);
            stackSize.y = Math.max(0, stackSize.y);
            stackSize.z = Math.max(0, stackSize.z);
        }
        if( layout != null ) {
            layout.reshape(offset, stackSize);
        }
        
        if( listeners != null ) {
            // Call the listeners with the original size befoe
            // the components took a whack at it.
            for( GuiControlListener l : listeners.getArray() ) {
                l.reshape(this, offset, size);
            }
        }
    }

However, there is something strange, the size is passed to listeners with the value previous to applying the insets but the offset isn’t. So we end with listeners that give a position applying insets and a size that doesn’t.

Shouldn’t that method use the “stackSize” instead of “size”? (the latter can be taken with source.getSize())

1 Like

No. Size is right but offset isn’t.

The listener reshape is on the GUI element… not on the component stack. There could be 100 items in that component stack doing strange things to the offset and size. The listeners should not see that. (Else you will see really strange offsets and sizes and certainly containing more than just the insets.)

In your case, just see if the element you are listening to has insets and offset accordingly. There really isn’t any other way as the component stack itself has no concept of insets… that’s just another component in the stack (and an InsetsComponent could technically be in multiple places in the stack).

Since you want to position your other GUI element relative to the upper inset corner then you will need to care about insets… by calling getInsets() (or getInsetsComponent()). A lot of other GuiControlListeners wouldn’t care about that.

Anyway, passing “offset” is technically a bug but it’s also safe to ignore in your listener. Because if the “right” value were passed in then it would just be new Vector3f() anyway.

1 Like

I’m not totally sure about that. It is a common case (at least I want to do it in a lot of places) to try to visually locate a panel in a position related to another one, considering that panel the visual part of it, what the background shows and, if I’m correct, the background is applying that 100 items transformations to it (so, the second panel position must be aware of that 100 items transformations too).

1 Like

100 items could be an icon or text or various other things that slowly squeeze the size more and more… (to the point that if you decided to locate your other element there it would be a tiny dot in the middle.)

Any of which are easy to get just by asking the component stack.

the GuiControlListener is meant to listen to…ahem… GuiControl. The size of the GuiControl is the one provided.

1 Like

Well, thanks for the info. I did a fast workaround trying to make it work with any insets type (I’m not sure it is right :sweat_smile: but it works with normal insets):

public void reshape(GuiControl source, Vector3f pos, Vector3f size) {
    Vector3f aux = optionsPanel.getLocalTranslation();

    InsetsComponent insetsComponent = source.getComponent(LAYER_INSETS);
    if(insetsComponent != null) {
        Vector3f position = new Vector3f();
        insetsComponent.reshape(aux.set(size.x, 0, 0), position);
        aux.addLocal(position.x, 0, 0);
    }

    optionsPanel.setLocalTranslation(aux.addLocal(0, 0, pos.z));
}
1 Like

Can we take a step back for a second… what is it that you are actually doing?

1 Like

To have the orange panel in the corner of the blue panel. In the code above the orange size /2 is missing but it is the less important part.

1 Like

Is it supposed to be overlapping like that or completely overlay?

1 Like

It is supposed to be just like in the image. The blue panel has insets and what the user sees is just it background. The orange panel top-center must be aligned with the blue panel top-right corner.

1 Like

What is the purpose of the insets in this case? (Since I can’t see the rest of the UI it is not obvious to me.)

1 Like

Well… now you mention it, nothing that couldn’t be done using panels instead of insets.

The blue and orange box are inside another container (the green one) with a SpringGridLayout. The green panel is the second element in that layout (the red is the first). Finally, the blue panel must be slightly separated from the surrounding borders.

1 Like