Tile Layout
import java.awt.*;
import java.util.LinkedList;
import java.io.Serializable;
/*
* Relative Component Layout Manager
* Copyright (c) 1999 John Catherino
* The cajo project: https://cajo.dev.java.net
*
* For issues or suggestions mailto:cajo@dev.java.net
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License, at version
* 2.1 of the licence, or any later version published by the Free Software
* Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You can receive a copy of the GNU Lesser General Public License from their
* website, http://fsf.org/licenses/lgpl.html; or via snail mail, Free
* Software Foundation Inc., 51 Franklin Street, Boston MA 02111-1301, USA
*/
/**
* A rather unique LayoutManager. In addition to laying out components relative
* to the container, it also supports layout relative to components <i>within</i>
* the container. Its purpose is to support arbitrarily complex component
* layouts, of an unlimited number of components, within a single container. It
* can easily create complex layouts that would otherwise require many
* subpanels, and multiple standard layout managers. It also can create layouts
* that are completely <i>impossible</i>, with standard layout managers. These
* features make this layout manager extremely flexible, and makes
* advancedlayouts extremely fast. It just may be, the last and only
* LayoutManager you'll ever need.
* <p>
*
* Components can be laid out above, below, left, or right of either a
* referenced component in the panel, or to the panel itself. Its width and
* height can be specified with similar flexibility. Absolute and proportional
* bounds are also supported. In typical use, one or more <i>'reference'</i>
* tiles are laid, and the rest of the components are set relative to them.
* <p>
*
* Usage example:<blockquote><tt><pre>
* panel.add(new JLabel("Label text:"), new Object[]{
* new Integer(TileLayout.LEFTINDENT + TileLayout.NOOFFSET +
* TileLayout.PROPWIDTH + TileLayout.FULLHEIGHT), refComponent,
* new Insets(-5, 10, 5, 10), new Rectangle(0, 0, 333, 0)
* // proportion rectangle 33.3%w
* });
* </tt></pre>
*
* </blockquote>
* <p>
*
* Up to four alignment constraints can be specified, their order does not
* matter:
*
* <p>
* <ul>
* <li> Positioning constants for indent, offset, width, and height
* <li> A Component for placement relative to, otherwise the container
* <li> A Rectangle for fixed and proportional component bounds
* <li> Insets to trim the resulting component boundaries
* </ul>
* <p>
*
* <u><i>Note</u>:</i> since the JRE <i>draws</i> components from last added
* to first; the manager lays them out similarly. Therefore, in order to layout
* relative to another component, the reference component must be added <u>after</u>
* the dependent one. I know this can be a drag at first, but it becomes
* critically important when components are laid out on top of others, or
* overlapping. This is a <i>super cool</i> capability of this layout manager.
*
* @version 1.0, 01-Nov-99 Initial release
* @author John Catherino
*/
public final class TileLayout implements LayoutManager2, Serializable {
private static final long serialVersionUID = 1L;
private static final Dimension NONE = new Dimension();
private final LinkedList components = new LinkedList();
private final LinkedList constraints = new LinkedList();
private void align(Dimension cont, Object cons[], Component comp) {
int align = 0;
Insets insets = null;
Rectangle tile = null, fixd = null;
if (cons != null) {
for (int i = 0; i < cons.length; i++) { // gather constraints
if (cons[i] != null) {
if (cons[i] instanceof Rectangle) fixd = (Rectangle)cons[i];
else if (cons[i] instanceof Insets) insets = (Insets)cons[i];
else if (cons[i] instanceof Integer) align = ((Integer)cons[i]).intValue();
else if (cons[i] instanceof Component) tile = ((Component)cons[i]).getBounds();
}
}
}
if (tile == null) tile = new Rectangle(cont);
Rectangle pref = new Rectangle(tile.getLocation(), comp.getPreferredSize());
// perform component positioning:
if ((align & 0x004000) != 0) pref.width = fixd.width;
else if ((align & 0x008000) != 0) pref.width = (tile.width * fixd.width + 500) / 1000;
else if ((align & 0x010000) != 0) pref.width = tile.width;
if ((align & 0x080000) != 0) pref.height = fixd.height;
else if ((align & 0x100000) != 0) pref.height = (tile.height
* fixd.height + 500) / 1000;
else if ((align & 0x200000) != 0) pref.height = tile.height;
if ((align & 0x000001) != 0) pref.x -= pref.width;
else if ((align & 0x000002) != 0) pref.x += (tile.width - pref.width >> 1);
else if ((align & 0x000004) != 0) pref.x += tile.width - pref.width;
else if ((align & 0x000008) != 0) pref.x += tile.width;
else if ((align & 0x000010) != 0) pref.x += fixd.x;
else if ((align & 0x000020) != 0) pref.x += (tile.width * fixd.x + 500) / 1000;
if ((align & 0x000040) != 0) pref.y -= pref.height;
else if ((align & 0x000080) != 0) pref.y += (tile.height - pref.height >> 1);
else if ((align & 0x000100) != 0) pref.y += tile.height - pref.height;
else if ((align & 0x000200) != 0) pref.y += tile.height;
else if ((align & 0x000400) != 0) pref.y += fixd.y;
else if ((align & 0x000800) != 0) pref.y += (tile.height * fixd.y + 500) / 1000;
if ((align & 0x001000) != 0) pref.setBounds(0, pref.y, pref.x
+ pref.width, pref.height);
else if ((align & 0x002000) != 0) pref.width = cont.width - pref.x;
if ((align & 0x020000) != 0) pref.setBounds(pref.x, 0, pref.width, pref.y
+ pref.height);
else if ((align & 0x040000) != 0) pref.height = cont.height - pref.y;
if (insets != null) { // apply insets, if any:
pref.x += insets.left;
pref.y += insets.top;
pref.width -= insets.left + insets.right;
pref.height -= insets.top + insets.bottom;
}
// Note: this can cause surprising results, so use it, if you dare. :-)
// Honour component minimum size:
Dimension d = comp.getMinimumSize();
if (pref.width < d.width) pref.width = d.width;
if (pref.height < d.height) pref.height = d.height;
comp.setBounds(pref); // now the tile is set!
}
/**
* This constant specifies placement of the component even with the left
* border of the provided reference component, otherwise to the container
* itself.
*/
public static final int NOINDENT = 0x000000;
/**
* This constant specifies placement of the component with its right border
* one pixel to the left of the provided reference component, otherwise to
* the container itself.
*/
public static final int LEFTINDENT = 0x000001;
/**
* This constant specifies placement of the component evenly between the left
* and right borders of the provided reference component, otherwise to the
* container itself.
*/
public static final int CENTERINDENT = 0x000002;
/**
* This constant specifies placement of the component with its right border
* evenly aligned with the right border of the provided reference component,
* otherwise to the container itself.
*/
public static final int FULLINDENT = 0x000004;
/**
* This constant specifies placement of the left border of the component one
* pixel to the right of the provided reference component origin, otherwise
* to the container itself.
*/
public static final int RIGHTINDENT = 0x000008;
/**
* Used in conjunction with a Rectangle constraint, the x value represents
* the desired component indent as a fixed number of pixels right of the
* provided component origin, otherwise to the container itself.
*/
public static final int FIXEDINDENT = 0x000010;
/**
* Used in conjunction with a Rectangle constraint, the x value represents
* the desired component indent as a percentage (2000 = 200.0%) of a provided
* component, otherwise to the container itself.
*/
public static final int PROPINDENT = 0x000020;
/**
* This constant specifies placement of the component even with the top
* border of the provided reference component, otherwise to the container
* itself.
*/
public static final int NOOFFSET = 0x000000;
/**
* This constant specifies placement of the component with its bottom border
* one pixel above the provided reference component, otherwise to the
* container itself.
*/
public static final int TOPOFFSET = 0x000040;
/**
* This constant specifies placement of the component evenly between the top
* and bottom borders of the provided reference component, otherwise to the
* container itself.
*/
public static final int CENTEROFFSET = 0x000080;
/**
* This constant specifies placement of the component with its bottom border
* evenly aligned with the bottom border of the provided reference component,
* otherwise to the container itself.
*/
public static final int FULLOFFSET = 0x000100;
/**
* This constant specifies placement of the top border of the component one
* pixel below the provided reference component origin, otherwise to the
* container itself.
*/
public static final int BOTTOMOFFSET = 0x000200;
/**
* Used in conjunction with a Rectangle constraint, the y value represents
* the desired component offset as a fixed number of pixels below the
* provided component origin, otherwise to the container itself.
*/
public static final int FIXEDOFFSET = 0x000400;
/**
* Used in conjunction with a Rectangle constraint, the y value represents
* the desired component offset as a percentage (2000 = 200.0%) of a provided
* component, otherwise to the container itself.
*/
public static final int PROPOFFSET = 0x000800;
/**
* This constant specifies that the component have its preferred width, as
* returned by its getPreferredSize method.
*/
public static final int PREFWIDTH = 0x000000;
/**
* This constant specifies that the component be made wide enough such that
* given its computed right margin, its left margin will align with that of
* its container.
*/
public static final int CLAMPLEFT = 0x001000;
/**
* This constant specifies that the component be made wide enough such that
* given its computed origin, its right margin will align with that of its
* container.
*/
public static final int CLAMPRIGHT = 0x002000;
/**
* Used in conjunction with a Rectangle constraint, the width value
* represents the desired component width as a fixed number of pixels.
*/
public static final int FIXEDWIDTH = 0x004000;
/**
* Used in conjunction with a Rectangle constraint, the width value
* represents the desired component offset as a percentage (2000 = 200.0%) of
* a provided component, otherwise to the container itself.
*/
public static final int PROPWIDTH = 0x008000;
/**
* This constant specifies that the component be made exactly as wide as its
* reference component, otherwise as the container itself.
*/
public static final int FULLWIDTH = 0x010000;
/**
* This constant specifies that the component have its preferred height, as
* returned by its getPreferredSize method.
*/
public static final int PREFHEIGHT = 0x000000;
/**
* This constant specifies that the component be made high enough such that
* given its computed bottom margin, its top margin will align with that of
* its container.
*/
public static final int CLAMPTOP = 0x020000;
/**
* This constant specifies that the component be made high enough such that
* given its computed origin, its bottom margin will align with that of its
* container.
*/
public static final int CLAMPBOTTOM = 0x040000;
/**
* Used in conjunction with a Rectangle constraint, the height value
* represents the desired component width as a fixed number of pixels.
*/
public static final int FIXEDHEIGHT = 0x080000;
/**
* Used in conjunction with a Rectangle constraint, the height value
* represents the desired component offset as a percentage (2000 = 200.0%) of
* a provided component, otherwise to the container itself.
*/
public static final int PROPHEIGHT = 0x100000;
/**
* This constant specifies that the component be made exactly as high as its
* reference component, otherwise as the container itself.
*/
public static final int FULLHEIGHT = 0x200000;
/**
* A frequently used position, used in conjunction with a reference
* component, it will locate the component directly over, with the same width
* and height.
*/
public static final Integer ABOVE = new Integer(NOINDENT + TOPOFFSET
+ FULLWIDTH + FULLHEIGHT);
/**
* A frequently used position, used in conjunction with a reference
* component, it will locate the component directly beneath, with the same
* width and height.
*/
public static final Integer BELOW = new Integer(NOINDENT + BOTTOMOFFSET
+ FULLWIDTH + FULLHEIGHT);
/**
* A frequently used position, used in conjunction with a reference
* component, it will locate the component directly adjacent, to its right,
* with the same width and height.
*/
public static final Integer RIGHT = new Integer(RIGHTINDENT + NOOFFSET
+ FULLWIDTH + FULLHEIGHT);
/**
* A frequently used position, used in conjunction with a reference
* component, it will locate the component directly adjacent, to its left,
* with the same width and height.
*/
public static final Integer LEFT = new Integer(LEFTINDENT + NOOFFSET
+ FULLWIDTH + FULLHEIGHT);
/**
* A frequently used position, it will render the component using the exact
* size of the reference component, or container.
*/
public static final Integer FULLSIZE = new Integer(NOINDENT + NOOFFSET
+ FULLWIDTH + FULLHEIGHT);
/**
* Nothing is performed in the constructor, it is bodyless.
*/
public TileLayout() {}
/**
* The primary component addition method. Components are added for layout,
* and its constraints are provided as an Object array. Up to 4 constraints
* may be specified, and their order is not important. The following are the
* applicable constraints:
* <ul>
* <p>
* <li> Rectangle for fixed or proportional component bounds
* <li> Insets to trim the final boundaries after computation
* <li> Integer of TileLayout positioning constants
* <li> Component for relative placement, instead of container
* </ul>
*
* @param comp The component to be laid out within the container.
* @param cons An Object[] containing 1 or more constraints.
*/
public void addLayoutComponent(Component comp, Object cons) {
components.add(comp);
constraints.add(cons);
}
/**
* This method will make the added component exactly the same size as the
* container itself. It is useful for adding wallpaper.
*
* @param comp The component to fill the container.
* @param name The name of the component, unused by TileLayout.
*/
public void addLayoutComponent(String name, Component comp) {
components.add(comp);
constraints.add(new Object[]{ FULLSIZE });
}
/**
* As expected, removes the component from the ordered layout list.
*/
public void removeLayoutComponent(Component comp) {
for (int i = 0; i < components.size(); i++) {
if (components.get(i) == comp) {
components.remove(i);
constraints.remove(i);
return;
}
}
throw new IllegalArgumentException("Component not in container");
}
/**
* Lays out the components, last added to first added, according to their
* specified constraints.
*/
public void layoutContainer(Container parent) {
Dimension container = parent.getSize();
for (int i = components.size() - 1; i >= 0; i--)
align(container, (Object[])constraints.get(i),
(Component)components.get(i));
}
/**
* Bodyless implementation, as TileLayout uses no cached information.
*/
public void invalidateLayout(Container target) {}
/**
* Indicate center alignment.
*/
public float getLayoutAlignmentX(Container target) { return 0.5F; }
/**
* Indicate center alignment.
*/
public float getLayoutAlignmentY(Container target) { return 0.5F; }
/**
* Indicates no minimum size.
*/
public Dimension minimumLayoutSize(Container parent) { return NONE; }
/**
* Indicates no maximum size.
*/
public Dimension maximumLayoutSize(Container parent) { return NONE; }
/**
* Indicates no preferred size.
*/
public Dimension preferredLayoutSize(Container parent) { return NONE; }
}
Related examples in the same category