JLayeredPane with a LayoutManager

constraints, java, jlayeredpane, layout-manager, swing

Solution

Based on the comment of Hovercraft Full Of Eels, I'll answer my own question.

Calling `JLayeredPane.add(Component comp, Object constraints)` will call `addImpl(Component comp, Object constraints, int index)`. This method is overridden by JLayeredPane itself, here's the source:

protected void addImpl(Component comp, Object constraints, int index) {
    int layer;
    int pos;

    if(constraints instanceof Integer) {
        layer = ((Integer)constraints).intValue();
        setLayer(comp, layer);
    } else
        layer = getLayer(comp);

    pos = insertIndexForLayer(layer, index);
    super.addImpl(comp, constraints, pos);
    comp.validate();
    comp.repaint();
    validateOptimizedDrawing();
}

As you can see, the `Integer` object is intercepted, so that the JLayeredPane knows the layer in which `comp` should be placed. Then, it passes on `constraints`, the Integer, to the super implementation. The essential part in the super implementation, `Container`, is:

protected void addImpl(Component comp, Object constraints, int index) {
    ...
    if (layoutMgr != null) {
        if (layoutMgr instanceof LayoutManager2) {
           ((LayoutManager2)layoutMgr).addLayoutComponent(comp, constraints);
        } else if (constraints instanceof String) {
           layoutMgr.addLayoutComponent((String)constraints, comp);
        }
    }
    ...
}

The solution would therefore be to extend `JLayeredPane` and override its `addImpl(Component comp, Object constraints, int index)` method, which can accept any Object of your choice: this object should contain the `Integer` needed for the JLayeredPane's layer decision, and it should also contain the constraints object for the chosen LayoutManager.

StackConstraints:

public final class StackConstraints {
    public final int layer;
    public final Object layoutConstraints;
    public StackConstraints (int layer, Object layoutConstraints){
        this.layer = layer;
        this.layoutConstraints = layoutConstraints;
    }
}

JLayeredPane extension:

protected void addImpl(Component comp, Object constraints, int index) {
    int layer;
    int pos;
    Object constr;
     if(constraints instanceof StackConstraints) {
        layer = constraints.layer.intValue();
        constr = ((StackConstraints) constraints).layoutConstraints;
        setLayer(comp, layer);
    } else {
        layer = getLayer(comp);
        constr = constraints;
    }

    pos = insertIndexForLayer(layer, index);
    super.addImpl(comp, constraints, pos);
    comp.validate();
    comp.repaint();
    validateOptimizedDrawing();

}

Problem

The situation: drawing a stack of playing cards, like in the Solitaire game. Nicely stacked. To achieve this, I'm using a `JLayeredPane` in combination with a custom implementation of the `LayoutManager` interface. The reason for using a custom LayoutManager is that the stack orientation varies, sometimes the playing cards cover each other completely, sometimes partially, and this logic seems to be a good job for a LayoutManager, because this basically boils down to setting the location of the cards. So, the `LayoutManager` is responsible for setting the X- and Y-coordinates of all components in my stack. The `JLayeredPane` on the other hand is responsible for their Z-coordinates (via its layers). Adding a component to a `JLayeredPane` goes like this: ``` JLayeredPane pane = new JLayeredPane(); pane.setLayout(new StackLayout(...)); pane.add(new CardView(...), new Integer(j)); ``` where `new Integer(j)` is the layer of the card. This must be an `Integer` due to the contract of `JLayeredPane`. The problem here is, that my `StackLayout` cannot have any other constraint object than an `Integer`, due to the reason stated above. The `LayoutManager` interface requires you to implement the following method: ``` addLayoutComponent(Component comp, Object constraints); ``` and the passed `Object` will here always be an `Integer`. In my particular situation, I am lucky, as my XY-coordinates can be calculated based on the Z-coordinates. For example, the card in layer `k` has to be located at Y-coordinate `k * offset`. So in my situation, the constraints object being an `Integer` is not a problem. I was wondering what you should be doing when there is no correlation between the Z-coordinates and the XY-coordinates? How can you solve this then? For example, how would I use a `GridBagLayout` in combination with a `JLayeredPane`, where the first requires a `GridBagConstraints` object and the second an `Integer` object? Of course, a GBL will layout in such a way that components do not overlap, but it's just the idea.

Original source