Saturday, March 05, 2016

some java hacking

For some reason I got motivated to investigate this little problem. Finally. After I don't know how many years...

java.awt.BorderLayout doesn't let you put things in the corners.

The corners don't even exist in their own right.

All that exists are a bar across the top, full width, a similar bar at the bottom, and left/right sections in between those bars, and then the center that resizes to fill the gap.

 But what about when you want those corners?
 This is one approach:


It resembles the desired result, but is fairly hackish by using nested BorderLayouts. Ugh.
 Google "borderlayout corner" and you get a bunch of non-answers, all very hackish, all of which purport to do what you want, but have various drawbacks ("I would use nested JPanels with such-n-such" or some other garbage). Stackoverflow is where most of the discussion takes place, and the result is typical of SOF. GridBag (yuk), the above nested BorderLayouts, "put your widgets into a custom Border", etc; all yuk, all missing the target.

The basic answer is that there's no built-in way in awt/swing to do this. At all. Which is kinda sad.

Actually the custom border is almost the right approach.

-------------------

I had wanted this years ago, long enough back that I don't remember when the idea first occurred to me.

Should not take an Advanced Degree(tm) to figure this out. Altho looking at that SOF discussion, it might. Fortunately that's what we have here at Hyde U.

-------------------

The answer is of course that you have to make your own layout manager. That's not terribly hard, I've done it before, with a grid variation.




The NE corner is a JButton. The other corners are JLabels. The sides are JPanels, with one JLabel each. The center is just a JPanel. All with background colors that are hideous so you can see where the boundaries are. The center/east boundary appears to have a one-pixel glitch I haven't figured out.

In the code below, it's ok to only fill one corner. Remember that JButton uses a lot of margin space, so if you wanted (for example, and the reason I started on this finally) a tiny little "X" button @ NE, you're going to have to force the JButton to be the size you want.

All the proper resizing takes place, layout is dynamically computed to match whatever you are putting in the corners.

Of course this leads to a fairly horrible aesthetic around the sides because the corners can force the sides into sizes you don't like, but the whole thing DOES work. If you want something sized more like the first image, then you should go with that alternative. It's in the code at the bottom.

How'd I do this? I grabbed the Java 7 source for BorderLayout, and started banging on it. It's not quite finished, I want to strip out some extraneous garbage (the LTR stuff, and the PAGE_START stuff), hgap/vgap aren't used everywhere as spacers...

Took me about 3 hours. Longer than it should have, I went down a bad direction with cut-n-paste errors, had to start over.

Here it is:
(note that this is at least partially copyright Sun Microsystems, from way back when; you can find the Java 7 source code easily enough, and see the original I started from)

--------

package com.hu.Test;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;

// HU: jeez, I should have written this over a decade ago. Well, finally, here it is.

/**
 * A border layout lays out a container, arranging and resizing
 * its components to fit in five regions:
 * north, south, east, west, and center.
 *
 * This modified version ALSO allows you to use the corners to hold other components. HU, Mar, 2016.
 *
 * Each region may contain no more than one component, and
 * is identified by a corresponding constant:
 * NORTH, SOUTH, EAST,
 * WEST, and CENTER.  When adding a
 * component to a container with a border layout, use one of these
 * five constants, for example:
 *
 *    Panel p = new Panel();
 *    p.setLayout(new BorderLayout());
 *    p.add(new Button("Okay"), BorderLayout.SOUTH);
 * 

 * As a convenience, BorderLayout interprets the
 * absence of a string specification the same as the constant
 * CENTER:
 *
 *    Panel p2 = new Panel();
 *    p2.setLayout(new BorderLayout());
 *    p2.add(new TextArea());  // Same as p.add(new TextArea(), BorderLayout.CENTER);
 * 

 *
 * NOTE: Currently (in the Java 2 platform v1.2),
 * BorderLayout does not support vertical
 * orientations.  The isVertical setting on the container's
 * ComponentOrientation is not respected.
 *
 * The components are laid out according to their
 * preferred sizes and the constraints of the container's size.
 * The NORTH and SOUTH components may
 * be stretched horizontally; the EAST and
 * WEST components may be stretched vertically;
 * the CENTER component may stretch both horizontally
 * and vertically to fill any space left over.
 *
 *
 * @author      Arthur van Hoff (original), Hyde University (corners)
 * @see         java.awt.Container#add(String, Component)
 * @see         java.awt.ComponentOrientation
 * @see            java.awt.BorderLayout
 * @since       JDK1.0
 */
public class BorderLayout implements LayoutManager2, java.io.Serializable {
    /**
     * Constructs a border layout with the horizontal gaps
     * between components.
     * The horizontal gap is specified by hgap.
     *
     * @see #getHgap()
     * @see #setHgap(int)
     *
     * @serial
     */
    int hgap;

    /**
     * Constructs a border layout with the vertical gaps
     * between components.
     * The vertical gap is specified by vgap.
     *
     * @see #getVgap()
     * @see #setVgap(int)
     * @serial
     */
    int vgap;

    /**
     * Constant to specify components location to be the
     *      north portion of the border layout.
     * @serial
     * @see #getChild(String, boolean)
     * @see #addLayoutComponent
     * @see #getLayoutAlignmentX
     * @see #getLayoutAlignmentY
     * @see #removeLayoutComponent
     */
    Component north;
    /**
     * Constant to specify components location to be the
     *      west portion of the border layout.
     * @serial
     * @see #getChild(String, boolean)
     * @see #addLayoutComponent
     * @see #getLayoutAlignmentX
     * @see #getLayoutAlignmentY
     * @see #removeLayoutComponent
     */
    Component west;
    /**
     * Constant to specify components location to be the
     *      east portion of the border layout.
     * @serial
     * @see #getChild(String, boolean)
     * @see #addLayoutComponent
     * @see #getLayoutAlignmentX
     * @see #getLayoutAlignmentY
     * @see #removeLayoutComponent
     */
    Component east;
    /**
     * Constant to specify components location to be the
     *      south portion of the border layout.
     * @serial
     * @see #getChild(String, boolean)
     * @see #addLayoutComponent
     * @see #getLayoutAlignmentX
     * @see #getLayoutAlignmentY
     * @see #removeLayoutComponent
     */
    Component south;
    /**
     * Constant to specify components location to be the
     *      center portion of the border layout.
     * @serial
     * @see #getChild(String, boolean)
     * @see #addLayoutComponent
     * @see #getLayoutAlignmentX
     * @see #getLayoutAlignmentY
     * @see #removeLayoutComponent
     */
    Component center;

    Component northeast, southeast, northwest, southwest;

    /**
     * The north layout constraint (top of container).
     */
    public static final String NORTH  = "North";

    /**
     * The south layout constraint (bottom of container).
     */
    public static final String SOUTH  = "South";

    /**
     * The east layout constraint (right side of container).
     */
    public static final String EAST   = "East";

    /**
     * The west layout constraint (left side of container).
     */
    public static final String WEST   = "West";

    /**
     * The center layout constraint (middle of container).
     */
    public static final String CENTER = "Center";

    public static final String NORTHEAST = "Northeast";
    public static final String NORTHWEST = "Northwest";
    public static final String SOUTHEAST = "Southeast";
    public static final String SOUTHWEST = "Southwest";

    /*
     * JDK 1.1 serialVersionUID
     */
    private static final long serialVersionUID = -8658291919501921765L;

    /**
     * Constructs a new border layout with
     * no gaps between components.
     */
    public BorderLayout() {
        this(0, 0);
    }

    /**
     * Constructs a border layout with the specified gaps
     * between components.
     * The horizontal gap is specified by hgap
     * and the vertical gap is specified by vgap.
     * @param   hgap   the horizontal gap.
     * @param   vgap   the vertical gap.
     */
    public BorderLayout(int hgap, int vgap) {
        this.hgap = hgap;
        this.vgap = vgap;
    }

    /**
     * Returns the horizontal gap between components.
     * @since   JDK1.1
     */
    public int getHgap() {
        return hgap;
    }

    /**
     * Sets the horizontal gap between components.
     * @param hgap the horizontal gap between components
     * @since   JDK1.1
     */
    public void setHgap(int hgap) {
        this.hgap = hgap;
    }

    /**
     * Returns the vertical gap between components.
     * @since   JDK1.1
     */
    public int getVgap() {
        return vgap;
    }

    /**
     * Sets the vertical gap between components.
     * @param vgap the vertical gap between components
     * @since   JDK1.1
     */
    public void setVgap(int vgap) {
        this.vgap = vgap;
    }

    /**
     * Adds the specified component to the layout, using the specified
     * constraint object.  For border layouts, the constraint must be
     * one of the following constants:  NORTH,
     * SOUTH, EAST,
     * WEST, or CENTER,
     * NORTHEAST, NORTHWEST,
     * SOUTHEAST, SOUTHWEST
     *
     * Most applications do not call this method directly. This method
     * is called when a component is added to a container using the
     * Container.add method with the same argument types.
     * @param   comp         the component to be added.
     * @param   constraints  an object that specifies how and where
     *                       the component is added to the layout.
     * @see     java.awt.Container#add(java.awt.Component, java.lang.Object)
     * @exception   IllegalArgumentException  if the constraint object is not
     *                 a string, or if it not one of the five specified
     *              constants.
     * @since   JDK1.1
     */
    public void addLayoutComponent(Component comp, Object constraints) {
        synchronized (comp.getTreeLock()) {
            if ((constraints == null) || (constraints instanceof String)) {
                addLayoutComponent((String)constraints, comp);
            } else {
                throw new IllegalArgumentException("cannot add to layout: constraint must be a string (or null)");
            }
        }
    }

    /**
     * @deprecated  replaced by addLayoutComponent(Component, Object).
     */
    @Deprecated
    public void addLayoutComponent(String name, Component comp) {
        synchronized (comp.getTreeLock()) {
            /* Special case:  treat null the same as "Center". */
            if (name == null) {
                name = "Center";
            }

            /* Assign the component to one of the known regions of the layout.
             */
            if (CENTER.equals(name)) {
                center = comp;
            } else if (NORTH.equals(name)) {
                north = comp;
            } else if (SOUTH.equals(name)) {
                south = comp;
            } else if (EAST.equals(name)) {
                east = comp;
            } else if (WEST.equals(name)) {
                west = comp;

            } else if (NORTHEAST.equals(name)) {
                northeast = comp;
            } else if (SOUTHEAST.equals(name)) {
                southeast = comp;
            } else if (NORTHWEST.equals(name)) {
                northwest = comp;
            } else if (SOUTHWEST.equals(name)) {
                southwest = comp;

            } else {
                throw new IllegalArgumentException("cannot add to layout: unknown constraint: " + name);
            }
        }
    }

    /**
     * Removes the specified component from this border layout. This
     * method is called when a container calls its remove or
     * removeAll methods. Most applications do not call this
     * method directly.
     * @param   comp   the component to be removed.
     * @see     java.awt.Container#remove(java.awt.Component)
     * @see     java.awt.Container#removeAll()
     */
    public void removeLayoutComponent(Component comp) {
        synchronized (comp.getTreeLock()) {
            if (comp == center) {
                center = null;
            } else if (comp == north) {
                north = null;
            } else if (comp == south) {
                south = null;
            } else if (comp == east) {
                east = null;
            } else if (comp == west) {
                west = null;

            } else if (comp == northeast) {
                northeast = null;
            } else if (comp == southeast) {
                southeast = null;
            } else if (comp == northwest) {
                northwest = null;
            } else if (comp == southwest) {
                southwest = null;
            }

        }
    }

    /**
     * Gets the component that was added using the given constraint
     *
     * @param   constraints  the desired constraint, one of CENTER,
     *                       NORTH, SOUTH,
     *                       WEST, EAST,
     *                       NORTHEAST, NORTHWEST,
     *                       SOUTHEAST, SOUTHWEST
     * @return  the component at the given location, or null if
     *          the location is empty
     * @exception   IllegalArgumentException  if the constraint object is
     *              not one of the specified constants
     * @see     #addLayoutComponent(java.awt.Component, java.lang.Object)
     * @since 1.5
     */
    public Component getLayoutComponent(Object constraints) {

        if (CENTER.equals(constraints)) {
            return center;

        } else if (NORTH.equals(constraints)) {
            return north;
        } else if (SOUTH.equals(constraints)) {
            return south;
        } else if (WEST.equals(constraints)) {
            return west;
        } else if (EAST.equals(constraints)) {
            return east;

        } else if (NORTHEAST.equals(constraints)) {
            return northeast;
        } else if (SOUTHEAST.equals(constraints)) {
            return southeast;
        } else if (NORTHWEST.equals(constraints)) {
            return northwest;
        } else if (SOUTHWEST.equals(constraints)) {
            return southwest;

        } else {
            throw new IllegalArgumentException("cannot get component: unknown constraint: " + constraints);
        }
    }


    /**
     * Returns the component that corresponds to the given constraint location.
     *
     * @param   constraints     the desired absolute position, one of CENTER,
     *                          NORTH, SOUTH,
     *                          EAST, WEST,
     *                          NORTHEAST, NORTHWEST,
     *                          SOUTHEAST, SOUTHWEST
     * @param   target     the {@code Container} used to obtain
     *                     the constraint location based on the target
     *                     {@code Container}'s component orientation.
     * @return  the component at the given location, or null if
     *          the location is empty
     * @exception   IllegalArgumentException  if the constraint object is
     *              not one of the specified constants
     * @exception   NullPointerException  if the target parameter is null
     * @see     #addLayoutComponent(java.awt.Component, java.lang.Object)
     * @since 1.5
     */
    public Component getLayoutComponent(Container target, Object constraints) {
        //boolean ltr = target.getComponentOrientation().isLeftToRight();
        Component result = null;

        if (NORTH.equals(constraints)) {
            result = north;
        } else if (SOUTH.equals(constraints)) {
            result = south;
        } else if (WEST.equals(constraints)) {
            result = west;
        } else if (EAST.equals(constraints)) {
            result = east;
       
        } else if (NORTHEAST.equals(constraints)) {
            result = northeast;
        } else if (SOUTHEAST.equals(constraints)) {
            result = southeast;
        } else if (NORTHWEST.equals(constraints)) {
            result = northwest;
        } else if (SOUTHWEST.equals(constraints)) {
            result = southwest;

        } else if (CENTER.equals(constraints)) {
            result = center;

        } else {
            throw new IllegalArgumentException("cannot get component: invalid constraint: " + constraints);
        }

        return result;
    }


    /**
     * Gets the constraints for the specified component
     *
     * @param   comp the component to be queried
     * @return  the constraint for the specified component,
     *          or null if component is null or is not present
     *          in this layout
     * @see #addLayoutComponent(java.awt.Component, java.lang.Object)
     * @since 1.5
     */
    public Object getConstraints(Component comp) {
        //fix for 6242148 : API method java.awt.BorderLayout.getConstraints(null) should return null
        if (comp == null){
            return null;
        }
        if (comp == center) {
            return CENTER;
        } else if (comp == north) {
            return NORTH;
        } else if (comp == south) {
            return SOUTH;
        } else if (comp == west) {
            return WEST;
        } else if (comp == east) {
            return EAST;

        } else if (comp == northwest) {
            return NORTHWEST;
        } else if (comp == southwest) {
            return SOUTHWEST;
        } else if (comp == northwest) {
            return NORTHWEST;
        } else if (comp == southwest) {
            return SOUTHWEST;
        }
        return null;
    }

    /**
     * Determines the minimum size of the target container
     * using this layout manager.
     *
     * This method is called when a container calls its
     * getMinimumSize method. Most applications do not call
     * this method directly.
     * @param   target   the container in which to do the layout.
     * @return  the minimum dimensions needed to lay out the subcomponents
     *          of the specified container.
     * @see     java.awt.Container
     * @see     java.awt.BorderLayout#preferredLayoutSize
     * @see     java.awt.Container#getMinimumSize()
     */
    public Dimension minimumLayoutSize(Container target) {
        synchronized (target.getTreeLock()) {
            Dimension dim = new Dimension(0, 0);

            boolean ltr = target.getComponentOrientation().isLeftToRight();
            Component c = null;

            if ((c=getChild(EAST,ltr)) != null) {
                Dimension d = c.getMinimumSize();
                dim.width += d.width + hgap;
                dim.height = Math.max(d.height, dim.height);
            }
            if ((c=getChild(WEST,ltr)) != null) {
                Dimension d = c.getMinimumSize();
                dim.width += d.width + hgap;
                dim.height = Math.max(d.height, dim.height);
            }
            if ((c=getChild(CENTER,ltr)) != null) {
                Dimension d = c.getMinimumSize();
                dim.width += d.width;
                dim.height = Math.max(d.height, dim.height);
            }
            if ((c=getChild(NORTH,ltr)) != null) {
                Dimension d = c.getMinimumSize();
                dim.width = Math.max(d.width, dim.width);
                dim.height += d.height + vgap;
            }
            if ((c=getChild(SOUTH,ltr)) != null) {
                Dimension d = c.getMinimumSize();
                dim.width = Math.max(d.width, dim.width);
                dim.height += d.height + vgap;
            }

            if ((c=getChild(NORTHEAST,ltr)) != null) {
                Dimension d = c.getMinimumSize();
                dim.width += d.width + hgap;
                dim.height = d.height + vgap;
            }

            Insets insets = target.getInsets();
            dim.width += insets.left + insets.right;
            dim.height += insets.top + insets.bottom;

            return dim;
        }
    }

    /**
     * Determines the preferred size of the target
     * container using this layout manager, based on the components
     * in the container.
     *
     * Most applications do not call this method directly. This method
     * is called when a container calls its getPreferredSize
     * method.
     * @param   target   the container in which to do the layout.
     * @return  the preferred dimensions to lay out the subcomponents
     *          of the specified container.
     * @see     java.awt.Container
     * @see     java.awt.BorderLayout#minimumLayoutSize
     * @see     java.awt.Container#getPreferredSize()
     */
    public Dimension preferredLayoutSize(Container target) {
        synchronized (target.getTreeLock()) {
            Dimension dim = new Dimension(0, 0);

            boolean ltr = target.getComponentOrientation().isLeftToRight();
            Component c = null;

            if ((c=getChild(EAST,ltr)) != null) {
                Dimension d = c.getPreferredSize();
                dim.width += d.width + hgap;
                dim.height = Math.max(d.height, dim.height);
            }
            if ((c=getChild(WEST,ltr)) != null) {
                Dimension d = c.getPreferredSize();
                dim.width += d.width + hgap;
                dim.height = Math.max(d.height, dim.height);
            }
            if ((c=getChild(CENTER,ltr)) != null) {
                Dimension d = c.getPreferredSize();
                dim.width += d.width;
                dim.height = Math.max(d.height, dim.height);
            }
            if ((c=getChild(NORTH,ltr)) != null) {
                Dimension d = c.getPreferredSize();
                dim.width = Math.max(d.width, dim.width);
                dim.height += d.height + vgap;
            }
            if ((c=getChild(SOUTH,ltr)) != null) {
                Dimension d = c.getPreferredSize();
                dim.width = Math.max(d.width, dim.width);
                dim.height += d.height + vgap;
            }

            if ((c=getChild(NORTHEAST,ltr)) != null) {
                Dimension d = c.getPreferredSize();
                dim.width += d.width + hgap;
                dim.height = d.height + vgap;
            }

            Insets insets = target.getInsets();
            dim.width += insets.left + insets.right;
            dim.height += insets.top + insets.bottom;

            return dim;
        }
    }

    /**
     * Returns the maximum dimensions for this layout given the components
     * in the specified target container.
     * @param target the component which needs to be laid out
     * @see Container
     * @see #minimumLayoutSize
     * @see #preferredLayoutSize
     */
    public Dimension maximumLayoutSize(Container target) {
        return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    /**
     * Returns the alignment along the x axis.  This specifies how
     * the component would like to be aligned relative to other
     * components.  The value should be a number between 0 and 1
     * where 0 represents alignment along the origin, 1 is aligned
     * the furthest away from the origin, 0.5 is centered, etc.
     */
    public float getLayoutAlignmentX(Container parent) {
        return 0.5f;
    }

    /**
     * Returns the alignment along the y axis.  This specifies how
     * the component would like to be aligned relative to other
     * components.  The value should be a number between 0 and 1
     * where 0 represents alignment along the origin, 1 is aligned
     * the furthest away from the origin, 0.5 is centered, etc.
     */
    public float getLayoutAlignmentY(Container parent) {
        return 0.5f;
    }

    /**
     * Invalidates the layout, indicating that if the layout manager
     * has cached information it should be discarded.
     */
    public void invalidateLayout(Container target) {
    }

    /**
     * Lays out the container argument using this border layout.
     *
     * This method actually reshapes the components in the specified
     * container in order to satisfy the constraints of this
     * BorderLayout object. The NORTH
     * and SOUTH components, if any, are placed at
     * the top and bottom of the container, respectively. The
     * WEST and EAST components are
     * then placed on the left and right, respectively. Finally,
     * the CENTER object is placed in any remaining
     * space in the middle.
     *
     * And the corners work as well, with NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST.
     *
     * Most applications do not call this method directly. This method
     * is called when a container calls its doLayout method.
     * @param   target   the container in which to do the layout.
     * @see     java.awt.Container
     * @see     java.awt.Container#doLayout()
     */
    public void layoutContainer(Container target) {
        synchronized (target.getTreeLock()) {
            Insets insets = target.getInsets();
            int top = insets.top;
            int bottom = target.getHeight() - insets.bottom;
            int left = insets.left;
            int right = target.getWidth() - insets.right;

            boolean ltr = target.getComponentOrientation().isLeftToRight();
            Component c = null;

            // order is somewhat important in the original, because there was an implied ordering to what was going on, top->bottom, L->R

            // now there's not.
           
            // we have to manage all 8 edge pieces together.
           
            Dimension zero = new Dimension(0, 0);
           
            Dimension nw = northwest != null ? northwest.getPreferredSize() : zero;
            Dimension no = north != null ? north.getPreferredSize() : zero;
            Dimension ne = northeast != null ? northeast.getPreferredSize() : zero;

            Dimension we = west != null ? west.getPreferredSize() : zero;
            Dimension ea = east != null ? east.getPreferredSize() : zero;

            Dimension sw = southwest != null ? southwest.getPreferredSize() : zero;
            Dimension so = south != null ? south.getPreferredSize() : zero;
            Dimension se = southeast != null ? southeast.getPreferredSize() : zero;
                       
            // boundary sizes.
           
            int lwid = Math.max(Math.max(nw.width, we.width), sw.width);
            int rwid = Math.max(Math.max(ne.width, ea.width), se.width);
            int thei = Math.max(Math.max(nw.height, no.height), ne.height);
            int bhei = Math.max(Math.max(sw.height, so.height), se.height);
           
            // each of these methods resizes the widget: setSize, setBounds

            if ((c=getChild(NORTH,ltr)) != null) {
                c.setBounds(left+lwid, top, right - left - lwid - rwid, thei);
            }
                       
            if ((c=getChild(SOUTH,ltr)) != null) {
                c.setBounds(left+lwid, bottom - bhei, right - left - lwid - rwid, bhei);
            }
           
            if ((c=getChild(EAST,ltr)) != null) {
                c.setBounds(right - rwid, top+thei, rwid, bottom - top - thei - bhei);
            }
           
            if ((c=getChild(WEST,ltr)) != null) {
                c.setBounds(left, top+thei, lwid, bottom - top - thei - bhei);
            }
           
            if ((c=getChild(CENTER,ltr)) != null) {
                c.setBounds(left + lwid, top + thei, right - left - rwid - lwid, bottom - top - thei - bhei);
            }
           
            if ((c=getChild(NORTHEAST,ltr)) != null) {
                c.setBounds(right-rwid, top, rwid, thei);
            }
           
            if ((c=getChild(SOUTHEAST,ltr)) != null) {
                c.setBounds(right-rwid, bottom - bhei, rwid, bhei);
            }
           
            if ((c=getChild(NORTHWEST,ltr)) != null) {
                c.setBounds(left, top, lwid, thei);
            }
            if ((c=getChild(SOUTHWEST,ltr)) != null) {
                c.setBounds(left, bottom-bhei, lwid, bhei);
            }
        }
    }

    /**
     * Get the component that corresponds to the given constraint location
     *
     * @param   key     The desired absolute position,
     *                  either NORTH, SOUTH, EAST, or WEST.
     *                  Or NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST.
     * @param   ltr     Is the component line direction left-to-right?
     */
    private Component getChild(String key, boolean ltr) {
        Component result = null;

        if (key == NORTH) {
            result = north;
        }
        else if (key == SOUTH) {
            result = south;
        }
        else if (key == WEST) {
            result = west;
        }
        else if (key == EAST) {
            result = east;
        }

        else if (key == NORTHWEST) {
            result = northwest;
        }
        else if (key == SOUTHWEST) {
            result = southwest;
        }
        else if (key == NORTHEAST) {
            result = northeast;
        }
        else if (key == SOUTHEAST) {
            result = southeast;
        }

        else if (key == CENTER) {
            result = center;
        }
        if (result != null && !result.isVisible()) {
            result = null;
        }
        return result;
    }

    /**
     * Returns a string representation of the state of this border layout.
     * @return    a string representation of this border layout.
     */
    public String toString() {
        return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
    }
}

--------------------
Here's the test case:

(There are four different approaches in here:)

package com.hu.Test;

//import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class BorderCorners {

    public static void main(String[] args) {
       
        JPanel mainPanel = new JPanel(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints(0, 0, 2,2, 1.0, 1.0,
                GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(
                        0, 0, 0, 0), 0, 0);

        mainPanel.add(new JLabel("Left Upper"), gbc);

        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.NORTHEAST;
        mainPanel.add(new JLabel("Right Upper"), gbc);

        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.anchor = GridBagConstraints.SOUTHWEST;
        mainPanel.add(new JLabel("Left Lower"), gbc);

        gbc.gridx = 2;
        gbc.gridy = 2;
        gbc.anchor = GridBagConstraints.SOUTHEAST;
        mainPanel.add(new JLabel("Right Lower"), gbc);

        JFrame frame = new JFrame("Grid Bag Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);

        // well, this ends up in the middle all right, but it doesn't size up/down when you resize the frame.
        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.red);
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.CENTER;
        mainPanel.add(centerPanel, gbc);


        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setVisible(true);

        // ***********************

        //yep, this misses the target too.

        JPanel northPanel = new JPanel(new java.awt.BorderLayout());     
        northPanel.add(new JLabel("North East"), java.awt.BorderLayout.EAST);
        northPanel.add(new JLabel("North West"), java.awt.BorderLayout.WEST);

        JPanel southPanel = new JPanel(new java.awt.BorderLayout());
        southPanel.add(new JLabel("South East"), java.awt.BorderLayout.EAST);
        southPanel.add(new JLabel("South West"), java.awt.BorderLayout.WEST);


        mainPanel = new JPanel(new java.awt.BorderLayout());
        mainPanel.add(northPanel, java.awt.BorderLayout.NORTH);
        mainPanel.add(southPanel, java.awt.BorderLayout.SOUTH);

        centerPanel = new JPanel();
        centerPanel.setBackground(Color.red);
        mainPanel.add(centerPanel, java.awt.BorderLayout.CENTER);

        JPanel left = new JPanel();
        left.setBackground(Color.YELLOW);
        mainPanel.add(left, java.awt.BorderLayout.WEST);

        JPanel right = new JPanel();
        right.setBackground(Color.GREEN);
        mainPanel.add(right, java.awt.BorderLayout.EAST);
       
        frame = new JFrame("AWT BorderLayout Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.setLocationByPlatform(true);
       
        frame.setSize(300, 300);
        frame.setVisible(true);

        // ***********************

        System.out.println("trying now with a copy of border layout");

        mainPanel = new JPanel(new BorderLayout());

        JButton xx = new JButton("X");
        System.out.println(xx.getInsets());
        Insets xxi = xx.getInsets();
        xxi.set(2, 2, 2, 2);
        xx.setMargin(xxi);
        //JLabel xx = new JLabel("X");
        xx.setOpaque(true);
        xx.setBackground(Color.MAGENTA);
        xx.setForeground(Color.white);

        mainPanel.add(xx, BorderLayout.NORTHEAST);
        mainPanel.add(new JLabel("North W"), BorderLayout.NORTHWEST);
        mainPanel.add(new JLabel("South East"), BorderLayout.SOUTHEAST);
        mainPanel.add(new JLabel("South W"), BorderLayout.SOUTHWEST);

        northPanel = new JPanel();
        northPanel.setBackground(Color.PINK);
        northPanel.add(new JLabel("N"));
        mainPanel.add(northPanel, BorderLayout.NORTH);

        southPanel = new JPanel();
        southPanel.setBackground(Color.CYAN);
        southPanel.add(new JLabel("S"));
        mainPanel.add(southPanel, BorderLayout.SOUTH);

        centerPanel = new JPanel();
        centerPanel.setBackground(Color.red);
        //mainPanel.add(centerPanel, BorderLayout.CENTER);
        mainPanel.add(centerPanel);
       
        left = new JPanel();
        left.setBackground(Color.YELLOW);
        left.add(new JLabel("W"));
        mainPanel.add(left, BorderLayout.WEST);

        right = new JPanel();
        right.setBackground(Color.GREEN);
        right.add(new JLabel("E"));
        mainPanel.add(right, BorderLayout.EAST);

        frame = new JFrame("NEW BorderLayout Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);

        frame.setSize(300, 300);
        frame.setVisible(true);
       
        // ***********************

        // this isn't right either. but it's slightly better.
        frame = new JFrame("Group L");


        JLabel labelNW = new JLabel("NW");
        JButton labelNE = new JButton("NE");
        JLabel labelSW = new JLabel("SW");
        JLabel labelSE = new JLabel("SE");


        GroupLayout layout = new GroupLayout(frame.getContentPane());
        layout.setHorizontalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(Alignment.LEADING)
                        .addComponent(labelNW)
                        .addComponent(labelSW))
                        .addGap(20,50,Short.MAX_VALUE)
                        .addGroup(layout.createParallelGroup(Alignment.TRAILING)
                                .addComponent(labelNE)
                                .addComponent(labelSE))
                );
        layout.setVerticalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(Alignment.LEADING)
                        .addComponent(labelNW)
                        .addComponent(labelNE))
                        .addGap(20,50,Short.MAX_VALUE)
                        .addGroup(layout.createParallelGroup(Alignment.TRAILING)
                                .addComponent(labelSW)
                                .addComponent(labelSE))
                );
        frame.getContentPane().setLayout(layout);
        frame.getContentPane().setBackground(Color.LIGHT_GRAY);
        frame.setSize(200, 200);
        frame.setVisible(true);

    }
}


No comments: