|
|||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | ||||||||||
java.lang.Objectnet.goui.awt.GoLayout
This class provides an intuitive and easy to specify way of controlling the layout of a fixed number of Components within a Container. Using the underlying GraphLayout class to control the layout procedure and with a simple and intuitive syntax for specifying the layout structure with a single String instance, this class is both easier to use and more powerful than the traditional AWT layout managers such as GridBagLayout.
Note however that this class does not address the problem of laying out an unknown number of Components in a regular formation and in these cases you should still use LayoutManagers such as GridLayout or FlowLayout.
The information required to define the layout of the Components is provided in a single String instance using the layout syntax described below. A simple BNF like form of the syntax will be given first and then each part will be examined in detail, along with several simple examples. Whitespace is ignored between elements but must not occur inside numeric values.
(" and ")" group elements.?" means "optional".*" means "zero or more"+" means "one or more"|" means "either of"[" and "]" form a regular expression style character class
Layout := ( Row )+
Row := ( "<" Weight Cell ( Edge? Cell )* ">" ) |
( "[" Cell ( Edge? Cell )* "]" ) |
( "*" )
Cell := ( "<" Alignment? Weight? ( ( ":" CompIndex ) | "*" )? ">" ) |
( "[" Alignment? ( CompIndex | "*" )? "]" ) |
( "(" ")" ) |
( "*" )
Edge := ( "|" ) |
( EdgeOffset? ( "v" | "^" ) RowOffset? ) |
( EdgeOffset? ":" RowIndex )
Alignment := [-^v|]
Weight, CompIndex, EdgeOffset, RowIndex := [0-9]+
RowOffset := "0"* [1-9] [0-9]*
[", "]"
form to specify a Row results in a weight of zero being used, whereas the
default value for the weight when using the "<",
">" form will be strictly positive.
The use of "*" to specify a Row is equivalent to
"<*>" and results in an empty Row with a weight of one.
This is useful as a space filling Row to facilitate the vertical
alignment of other Rows.
[", "]" form for a Cell is similar to that
for Rows and gives the Cell a weight of zero. Unlike a Row however, a Cell
can have a vertical alignment character specified before the weight value.
This is used during the vertical layout phase and is discussed below.
As well as having a weight, a Cell also has a Component index. This
is either "*" or a numeric value specified after a
":" in the Cell declaration. If the index is omitted
completely then the index is implicitly defined as the next available
index, starting from zero. If the index is specified explicitly then it
must be a non-negative integer and correspond to an index of a Component
to be laid out. It is not permitted to mix implicit and explicit indices
within a Layout. If the index is just given as "*" then the
Cell will not be associated with a Component and remain empty.
The use of "(", ")" to specify a Cell indicates
that the matching Cell from the Row above should be used at this point in
the Row. This 'cloning' operation is used to allow a single Cell to span
several Rows. However when a Cell is cloned, it is important that the Edges
on either side of the original Cell are the same as the Edges on either side
of the clone. For both the original and the clone Cell the Edges either
side must be explicitly declared.
The use of "*" to specify a Cell is equivalent to
"<*>" and results in an empty Cell with a weight of one. This
is useful as a space filling Cell to facilitate the horizontal alignment of
other Cells.
^",
"v" or ":" notation.
In order to be able to determine which Edge a given Edge reference refers to, a target Row must be specified. This is defined as follows:
^ N : Reference the Nth Row above this one (N > 0).v N : Reference the Nth Row below this one (N > 0).: N : Reference the Nth Row (N >= 0).:" a non-negative value
must be given which corresponds to a Row other than this one.
Once a target Row has been determined, a particular Edge can be found. Where no Edge offset is given, the Edge used will be the next available one in the target Row. If an Edge offset is supplied then that number of Edges (or Edge references) will be skipped first. For example, if we format the layout string so that the Rows are above each other:
A B C
"[ [ ] | [ ] | [ ] | [ ] ]"
"[ [ ] ^ [ ] ^ [ ] ^ [ ] ]"
A' B' C'
In this case we have three Edge anchors in the first Row which are
referenced from the second Row. If we now introduce a third Row,
we can reference Edges within it also:
A B' C
"[ [ ] | [ ] [ ] | [ ] ]"
"[ [ ] ^ [ ] v [ ] ^ [ ] ]"
"[ [ ] | [ [ ] ]"
A' B C'
Note that this is correct because C' references the next explicitly
defined Edge in the first Row (C) whereas B' references the first
explicitly defined Edge in the 3rd Row (B). However the following
example, while superficially similar, does not behave as expected.
A X C
"[ [ ] | [ ] | [ ] | [ ] ]"
"[ [ ] ^ [ ] v [ ] ^ [ ] ]"
"[ [ ] | [ ] ]"
A' B C'
Here C' will actually refer to the Edge reference X, because X is the
next explicitly defined Edge in the first Row. To make this example
work while retaining the existence of X, we can use an explicit
Edge offset "1^":
A X C
"[ [ ] | [ ] | [ ] | [ ] ]"
"[ [ ] ^ [ ] v [ ] 1^ [ ] ]"
"[ [ ] | [ ] ]"
A' B C'
This also highlights the difference between specifying an Edge anchor
"|" which has no references to it and not specifying an
Edge at all. During a layout they will behave the same, but an
explicitly defined Edge will change the behaviour of any Edge references
which reference that Row.
Essentially the layout process is divided into the horizontal layout phase and the vertical layout phase. In the horizontal layout, each Row is laid out according to the constraints placed upon it by the Cells within it and the placement of of Edges from earlier Row layout. The order in which the Rows are laid out is determined by looking at the dependencies between the Rows.
In order to avoid circular dependencies within the layout, there must be at least one Row which contains no Edge references, only Edge anchors or implicit Edge declarations; all such Rows are laid out first. The remaining Rows are laid out such that any Edge references they contain may only refer to Edges whose anchor was declared in Rows which have already been laid out. If the layout declaration string does not allow for the well ordering of the Rows in this way then an IllegalArgumentException will be throw during initialisation of the GoLayout. For example:
"[ [ ] | [ ] v [ ] ]" Row 0: References Row 1 "[ < > | [ ] ]" Row 1: Does not reference any other Row "[ [ ] ^2 < > ]" Row 2: References Row 0would result in the Rows being laid out in the order (1, 0, 2); whereas:
"[ [ ] | [ ] v < > ]" Row 0: References Row 1 "[ < > | [ ] v [ ] ]" Row 1: References Row 2 "[ [ ] ^2 < > | [ ] ]" Row 2: References Row 0causes an IllegalArgumentException to be thrown.
When laying out such a group, an attempt is made to give each Cell a fraction of the available space between the fixed Edges which is in proportion to their weight values. However the layout process will never resize a Cell below its minimum or above its maximum width (these values are calculated based upon the minimum, preferred and maximum sizes of the Components associated with each Cell).
Because the layout process will initially always attempt to give a Cell with zero weight a zero width, it will always end up being given its minimum width. Thus you can think of a zero weight Cell as a fixed width Cell, whose width is always the minimum width of the Cell. Any Cell whose weight is greater than zero can be thought of as a stretchy Cell.
If a group of Cells all have zero weight then it is possible that the layout process will not allocate all of the available space to the Cells. If this occurs then the Cells will be left-justified and any remaining space will go to the far end of the group.
If there is enough space within the Container to allow every Cell to be resized to the preferred width of its Component, then the minimum width of the Cell will simply be the preferred width of the associated Component. When there is no longer enough room to give each Cell the preferred width of its Component, then the minimum width of a Cell is calculated as a value between the minimum and preferred widths of its Component in proportion to the overall minimum and preferred width of the layout.
In other words, if the layout has a width which is half way between its minimum and preferred sizes, then each Cell will have a minimum width which is half way between the minimum and preferred sizes of its associated Component. Note however that this is not the same as simply taking a layout at the preferred size and shrinking it by some factor.
The minimum height of a Row is defined as the maximum of the set of minimum heights of all Cells which are defined only in this Row (remember that it is possible for a Cell to cover several Rows due to cloning). The maximum height of a Row is unbounded.
Furthermore because of the more limited control on the position of Cells in the vertical layout, the optional Cell alignment is used give more control regarding the position of the Cell's Component. The valid alignment characters are:
- : Centred vertically (VALIGN_CENTER)^ : Aligned to the top edge (VALIGN_TOP)v : Aligned to the bottom edge (VALIGN_BOTTOM)| : Filled vertically (VALIGN_FILLED)"[ < > ]" "< [ ] < > [ ] >" "[ < > ]"
" * " "[ [*] | <*> ]" <-- Zero height row "[ < > ^ < > ]" "[ < > ^ < > ]" "[ < > ^ < > ]" " * "
This is an interesting example because it introduces the idea of using
a zero height Row to facilitate layout control. The use of
"[*]" to define an empty, zero weight, Cell results in the
Edge anchor "|" being positioned as far to the left as
possible while complying with the minimum size constraints of all the
Cells below it. "<*>" is used here (rather than just
"*") as a stylistic point to clarify that this empty cell
has a vital positional role in this layout rather than just being empty
space.
"[ [ ] | < > ]" "[ ( ) ^ < > 1v < > ]" "<2 < > v [ ] v ( ) >" "<1 ( ) 1v ( ) v * v < > >" "[ * :0 < > 1v ( ) v [ ] | * ]" "[ * :0 <1*> | ( ) | <2 * > ]" <-- Zero height row
This layout mimics the "crazy" layout used in the Sun examples and the best way to seek to understand how it works is to use the example code in the GoLayoutTest class. Things worth noting in this example are:
( )"1v":0"
While clearly quite non-trivial in its complexity, this is still a lot simpler and easier to control than the equivalent implementation using GridBagLayout.
| Nested Class Summary | |
class |
GoLayout.Cell
|
class |
GoLayout.Row
|
| Field Summary | |
static int |
VALIGN_BOTTOM
Vertical alignment constant for Cells. |
static int |
VALIGN_CENTER
Vertical alignment constant for Cells. |
static int |
VALIGN_FILLED
Vertical alignment constant for Cells. |
static int |
VALIGN_TOP
Vertical alignment constant for Cells. |
static java.lang.String |
VERSION
Simple version identifier which can be used to ensure compatibility between versions. |
| Constructor Summary | |
GoLayout(java.lang.String layout)
This constructs a GoLayout instance using the given layout string and with a default weight of 1 and a default vertical alignment of VALIGN_FILLED. |
|
GoLayout(java.lang.String layout,
int weight,
int align)
This constructs a GoLayout instance given a layout string, default weight and default vertical alignment. |
|
| Method Summary | |
void |
addLayoutComponent(java.lang.String constraint,
java.awt.Component component)
This method is implemented as part of the LayoutManager interface but does nothing in GoLayout. |
java.awt.Dimension |
getEdgeSize()
This method returns the current Edge width and height. |
GoLayout.Row |
getRow(int index)
This method returns the Row instance corresponding to the specified index within the layout. |
int |
getRowCount()
This method returns the number of Rows in this layout. |
void |
layoutContainer(java.awt.Container parent)
This method lays out the child Components of the given Container instance in accordance with the layout constraints place upon it by the layout string and the minimum, preferred and maximum sizes of the Components being laid out. |
java.awt.Dimension |
minimumLayoutSize(java.awt.Container parent)
This method returns the minimum required size for a Container to ensure that all the laid out Components can receive their minimum size. |
java.awt.Dimension |
preferredLayoutSize(java.awt.Container parent)
This method returns the minimum required size for a Container to ensure that all the laid out Components can receive their preferred size. |
void |
removeLayoutComponent(java.awt.Component component)
This method is implemented as part of the LayoutManager interface but does nothing in GoLayout. |
void |
setEdgeSize(int width,
int height)
This method sets the Edge width and height for this layout. |
| Methods inherited from class java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
| Field Detail |
public static final java.lang.String VERSION
public static final int VALIGN_CENTER
public static final int VALIGN_TOP
public static final int VALIGN_BOTTOM
public static final int VALIGN_FILLED
| Constructor Detail |
public GoLayout(java.lang.String layout,
int weight,
int align)
layout - A valid layout string as described above (non-null).weight - A non-negative default weight for Rows and Cells.align - The default vertical alignment constant for Cells.
java.lang.IllegalArgumentException - if any of the parameters are
invalid.public GoLayout(java.lang.String layout)
layout - A valid layout string as described above (non-null).
java.lang.IllegalArgumentException - if any of the parameters are
invalid.| Method Detail |
public void setEdgeSize(int width,
int height)
Note that if a Cell is empty, inactive or has zero a width then the Edge sizes will not be applied. This permits the use of multiple successive empty Cells without issue and ensures that inactive Cells behaviour in the expected manner.
width - The non-negative Edge width in the layout.height - The non-negative Edge height in the layout.
java.lang.IllegalArgumentException - if either of the arguments are
negative.public java.awt.Dimension getEdgeSize()
public int getRowCount()
public GoLayout.Row getRow(int index)
index - The index of the Row to be returned.
java.lang.IndexOutOfBoundsException - if no such Row exists.public void layoutContainer(java.awt.Container parent)
layoutContainer in interface java.awt.LayoutManagerparent - The parent Container in which the Components which
are to be laid out reside.public java.awt.Dimension minimumLayoutSize(java.awt.Container parent)
In fact GoLayout will never size a Component below its minimum size, so if the parent Container is reduced below this size, some Components may be positioned outside the bounds of the parent and rendered partially of completely invisible.
minimumLayoutSize in interface java.awt.LayoutManagerparent - The parent Container in which the child Components
reside.public java.awt.Dimension preferredLayoutSize(java.awt.Container parent)
preferredLayoutSize in interface java.awt.LayoutManagerparent - The parent Container in which the child Components
reside.
public void addLayoutComponent(java.lang.String constraint,
java.awt.Component component)
addLayoutComponent in interface java.awt.LayoutManagerpublic void removeLayoutComponent(java.awt.Component component)
removeLayoutComponent in interface java.awt.LayoutManager
|
|||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | ||||||||||