OverlapLayout class is a layout manager that
* lays out a container's components in an overlapping fashion. A
* component can be painted "above" or "below" the previous component
* in the container.
*
* Like the GridLayout, each component is sized to the largest width and
* height of any component in the container. The amount of overlap is
* controlled by specifying the overlap postion of each component. You can
* simulate, left-to-right, right-to-left, top-to-bottom, bottom-to-top and
* diagonal layouts. As well you can "stack" components completely on top of
* one another. In this case the components are sized to the space available
* in the container.
*
* A main usage for this layout might be in the creation of "card games". A
* few features have been added that might be handy in these cases:
*
* a) a "popup" feature - when a component is selected in can "popup" from
* its regular location so it visibly stands out. To accomplish this some
* extra space must be reserved in the container for the popup. This is
* done by using the setPopupInsets method which allow you to control the
* popup direction. In addition you can add/remove a simple constraint to
* the component. POP_UP will popup the component. POP_DOWN or null
* will paint the component in its regular location.
* b) when a component is made "invisible" you can reserve its location in the
* container so all the other components don't shift.
*
* Note: this layout is achieved by changing the ZOrder of components in the
* container. It will not work for all components as some compnents will
* always paint themselves on the top of others. This seems to happen with
* components like JButton as rollover effects are painted when a mouse moves
* over the components.
*/
public class OverlapLayout implements LayoutManager2, java.io.Serializable
{
public static Boolean POP_UP = Boolean.TRUE;
public static Boolean POP_DOWN = Boolean.FALSE;
private static final int PREFERRED = 0;
private static final int MINIMUM = 1;
// Indicates how a component is painted
private boolean overlapAbove;
private Point overlapPosition;
// Reserve space for invisible components in the Container
private boolean includeInvisible = true;
// Reserve extra space so a component can "popup"
private Insets popupInsets = new Insets(0, 0, 0, 0);
// Track original order in which the components where added
private List* * @param target the container in which to do the layout */ public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { int size = components.size(); if (size == 0) return; // Determine the maximum size of the components Dimension maximumSize = new Dimension(); for (Component component: components) { if (component.isVisible() || includeInvisible) { Dimension preferred = component.getPreferredSize(); maximumSize.width = Math.max(preferred.width, maximumSize.width); maximumSize.height = Math.max(preferred.height, maximumSize.height); } } // Determine location of first component Point location = new Point(0, 0); Insets parentInsets = parent.getInsets(); // Layout right-to-left, else left-to-right if (overlapPosition.x < 0) location.x = parent.getWidth() - maximumSize.width - parentInsets.right - popupInsets.right; else location.x = parentInsets.left + popupInsets.left; // Layout bottom-to-top, else top-to-bottom if (overlapPosition.y < 0) location.y = parent.getHeight() - maximumSize.height - parentInsets.bottom - popupInsets.bottom; else location.y = parentInsets.top + popupInsets.top; // Set the size and location for each component for (int i = 0 ; i < size ; i++) { Component component = components.get(i); if (component.isVisible() || includeInvisible) { // When components are "stacked" resize each component to fill // the size of the parent container if (overlapPosition.x == 0 && overlapPosition.y == 0) { int width = parent.getWidth() - parentInsets.left - parentInsets.right; int height = parent.getHeight() - parentInsets.top - parentInsets.bottom; component.setSize(width, height); } else // resize each component to be the same size { component.setSize( maximumSize ); } // Set location of the component int x = location.x; int y = location.y; // Adjust location when component is "popped up" Boolean constraint = constraints.get(component); if (constraint != null && constraint == Boolean.TRUE) { x += popupInsets.right - popupInsets.left; y += popupInsets.bottom - popupInsets.top; } component.setLocation(x, y); // Calculate location of next component using the overlap offsets location.x += overlapPosition.x; location.y += overlapPosition.y; } } }} /** * There is no maximum. */ public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** * Returns the alignment along the x axis. Use center alignment. */ public float getLayoutAlignmentX(Container parent) { return 0.5f; } /** * Returns the alignment along the y axis. Use center alignment. */ 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) { // remove constraints here? } /** * Returns the string representation of this column layout's values. * @return a string representation of this grid layout */ public String toString() { return getClass().getName() + "[overlapAbove=" + overlapAbove + ",overlapPosition=" + overlapPosition + "]"; } }