Title: Layout
1Layout
- How multiple components are composed to build
interfaces
2Placement of widgets
- We need to be able to control the placement of
widgets on the screen - Need to be able to handle movement and
repositioning and keep these consistent - Keeping objects geometrically consistent is a
fundamental problem. - When the objects are widgets it is called the
layout problem. - When the objects are other things it is called
constraints.
3(No Transcript)
4Layout
- We need to programme interfaces to ensure all
widgets move appropriately. - Three basic algorithms underpin layout
- Fixed position
- Edge anchored
- Variable intrinsic size
- For each approach we need to consider
- what information must be stored with the widgets
from which the layout can be computed - what is the algorithm for computing the layout,
- how will the interface designer specify the
layout either interactively or programmatically.
5Fixed Position Layout
- Simplest layout approach assign every widget a
fixed rectangle - Coordinates relative to the parent
- Good for fixed interface that you do not want to
resize or whose components do not change.
6(No Transcript)
7- Simple rectangle for each widget
- Container widget sets the childs bounds location
to its own plus the childs
public class Widget other fields and methods
public Rectangle desiredBounds public void
doLayout(Rectangle newBounds)
setBounds(newBounds) foreach child widget C
Rectangle newChildBounds new
Rectangle(newBounds.leftC.desiredBounds.left,
newBounds.TopC.desiredBounds.
top, C.desiredBounds.width,
C.desiredBounds.height) C.doLayout(newChildBo
unds)
8Fixed Layout
- Advantages
- a simple algorithm
- minimal data
- very simple model to specify.
- Designing Programmatically
- construct a rectangle with the desired location
and size - set desiredBounds
- add the widget to its container.
- Disadvantage
- Resizing not handled at all well
- To small parts of components clipped or not shown
at all - To big all in the top corner
9Edge anchored layout
- Modifying widgets so that their edges can be
anchored to the edges of their containers
rectangle. - widget has coordinates for left, right, top and
bottom. - interpretation depends upon the boolean values
anchorLeft, anchorRight, anchorTop, anchorBottom.
- widget has a desiredWidth and desiredHeight.
- Edges are either anchored or not
- If anchored then it is a fixed distance away from
the corresponding container edge. - The algorithms for the X and Y axes are
independent and identical. - If only one edge is anchored, then the desired
width is used to determine the location of the
other edge. - If neither edge is anchored, then the left value
is used to compute a proportional distance
between the container edges and the desired width
is used for the other edge. - Both edges are anchored, they follow their
respective edges.
10Public class Widget . . . . other fields and
methods . . . public Rectangle bounds public
int left, right, top, bottom public boolean
anchorLeft, anchorRight, anchorTop, anchorBottom
public int width, height public const int
MAXSIZE4096 public void doLayout( Rectangle
newBounds) boundsnewBounds foreach
child widget C Rectangle childBoundsnew
Rectangle() if (C.anchorLeft)
childBounds.leftnewBounds.leftC.left if
(C.anchorRight) childBounds.rightnewBounds
.right-C.right else
childBounds.rightchildBounds.leftC.width
else if (C.anchorRight) // right is
anchored left is not childBounds.rightnewB
ounds.right-C.right childBounds.leftchildBo
unds.right-C.width else // neither
edge is anchored childBounds.left
newBounds.widthC.left/MAXSIZE
childBounds.rightchildBounds.leftC.width
. . . . perform similar computation for
Y . . . . C.doLayout(childBounds)
11Key concepts
- Container does the layout work on the widgets
children - To lay out a widget, a programmer sets the
various member fields and adds the widget to its
container. - The container then will make certain that the
widgets bounds are placed in the correct
position. - Programming this interactively requires a special
interface to highlight the edge relationships
and specify how they are bound
12(No Transcript)
13Variable intrinsic size layout
- On many occasions he designer does not want to
manually position the widgets - Create group and have them position themselves
- Menus
- Changes in language/font for labels
- Want to automatically adapt the layout to changes
in interface content - Each widget knows how much screen space it can
reasonably use (intrinsic size) - Each container has a plan for how to allocate
screen space based on the needs of its children. - Recursively arranging containers with various
layout plans - This is main approach used in Java
14Layout algorithm
- Based on two recursive passes
- first pass requests each widget to report its
desired size. - second pass sets the widget bounds
public void doLayout(Rectangle newBounds)
foreach child widget C ask for desired
size of C //based on desired sizes and
newBounds, decide where //each child should go
foreach child widget C C.doLayout( new
bounds for C)
15- Intrinsic size layouts ask a widget for
- minimum size (the smallest dimensions that it
can effectively use), - desired size (the dimensions that would work
best for this widget) - maximum size (the largest dimensions that it can
effectively use). - Determined by nature of each widget
- Containers calculate this based on the size of
its children
16- Widget class extended to handle sizes for layout
- Each component implements in own way
public class Dimension public int width
public int height public class Widget
. . . other methods and fields . . . public
Dimension getMinSize() . . . public
Dimension getDesiredSize() . . . public
Dimension getMaxSize() . . .
17Sizes of Simple Widgets
18- Desire sizes includes the margin
- Minimum size looses the margin
- Font details used to calculate size based on text
in label
public class Button . . . other methods and
fields . . . public Dimension getMinSize()
int minWidth bevelWidth2font.getLength(labelT
ext) int minHeight bevelWidth2font.getHeig
ht() return new Dimension(minWidth,minHeight)
public Dimension getDesiredSize() int
desWidth bevelWidth2marginWidth2font.getLeng
th(labelText) int minHeightbevelWidth2margi
nHeight2font.getHeight() return new
Dimension(desWidth,desHeight) public
Dimension getMaxSize() return
getDesiredSize()
19Simple container layouts
- The simplest containers are the vertical and
horizontal stack. Box widgets in Java
Horizontal Stack
A
B
C
D
Vertical Stack
20public class HorizontalStack public
Dimension getMinSize() int minWidth0
int minHeight0 foreach child widget C
Dimension childSize C.getMinSize()
minWidth childSize.width if
(minHeightltchildSize.height)
minHeightchildSize.height return new
Dimension(minWidth,minHeight) public
Dimension getDesiredSize() similar to
getMinSize using C.getDesiredSize() public
Dimension getMaxSize() similar to getMinSize
using C.getMaxSize()
21doLayout() for horizontal stack
- when a new window is opened
- request the desired size of the root widget and
allocate that as the window size. - BUT, there may not be enough screen space for a
window that big. - Space is allocated among the children depends
upon - whether the width is less than min
- greater than min but less than desired
- greater than desired but less than max
- greater than max.
- The idea of the algorithm is to give each child
as much as possible and then divide up the
remainder proportionally among the children
according to their requests.
22public class HorizontalStack . . . the other
methods and fields . . . public void
doLayout(Rectangle newBounds) Dimension min
getMinSize() Dimension desired
getDesiredSize() Dimension max
getMaxSize() If (min.widthgtnewBounds.width)
// give all children their minimum and let
them be clipped int childLeftnewBounds.left
foreach child widget C Rectangle
childBounds new Rectangle() childBounds.to
pnewBounds.top childBounds.heightnewBounds
.height childBounds.leftchildLeft
childBounds.width C.getMinSize().width
childLeftchildBounds.width
C.doLayout(childBounds)
23 else if (desired.widthgtnewBounds.width) //
give min to all and proportional on what is
available for desired int desiredMargin
desired.width-min.width float fraction
(float)(newBounds.width-min.width)/desiredMargin
int childLeftnewBounds.left foreach child
widget C Rectangle childBoundsnew
Rectangle() childBounds.topnewBounds.top
childBounds.heightnewBounds.height
childBounds.leftchildLeft int
minWidthC.getMinSize().width int
desWidthC.getDesiredSize().width
childBounds.widthminWidth(desWidth-minWidth)
fraction childLeftchildBounds.width
C.doLayout(childBounds)
24else // allocate what remains based on
maximum widths int maxMargin
max.width-desired.width float fraction
(float)(newBounds.width-desired.width)/maxMargin
int childLeftnewBounds.left foreach
child widget C . . . Similar code to
previous case . . .
25Combining layouts
- Use multiple horizontal and vertical stacks to
produce layouts
26Efficiency of layout
- Problem 1 Recursion The window will call the
root widget to ask for its desired size. The root
widget will recursively call all of its children
to compute that size. - When the root widget starts to do its layout it
will call the Palette groups desired sizes
again. - When the Palette group starts its own layout it
will call the Colors groups desired sizes yet
again - finally when the Colors group does its layout it
will call desired sizes on the text box. - The desired size methods on the Colors text box
was called at least 4 times.
- Problem 2 Changes. The size of a window may be
changed many times but the desires sizes of the
various widgets do not change often.Most systems
cache but what happens when things do change
- an invalidate() method is provided that informs
the parent widget that the desired sizes are no
longer correct. - invalidate() method not only sets its own sizes
to be invalid, but also sends the invalidate()
message to its container. - Java/Swing does not propagate invalidate() up the
tree causing interfaces not to change when they
might be expected to.
27public class HorizontalStack private
Dimension minSize private Dimension
desiredSize private Dimension maxSize
private boolean sizesAreValid public
Dimension getMinSize() if (sizesAreValid)
return minSize int minWidth0 int
minHeight0 foreach child widget C
Dimension childSize C.getMinSize()
minWidth childSize.width if
(minHeightltchildSize.height)
minHeightchildSize.height
sizesAreValidtrue return new
Dimension(minWidth,minHeight) public
Dimension getDesiredSize() similar to
getMinSize using C.getDesiredSize() public
Dimension getMaxSize() similar to getMinSize
using C.getMaxSize() public void
invalidate() sizesAreValidfalse if
(myContainer!null) myContainer.invalidate()
28public class HorizontalStack private
Dimension minSize private Dimension
desiredSize private Dimension maxSize
private boolean sizesAreValid public
Dimension getMinSize() if (sizesAreValid)
return minSize int minWidth0 int
minHeight0 foreach child widget C
Dimension childSize C.getMinSize()
minWidth childSize.width if
(minHeightltchildSize.height)
minHeightchildSize.height
sizesAreValidtrue return new
Dimension(minWidth,minHeight) public
Dimension getDesiredSize() similar to
getMinSize using C.getDesiredSize() public
Dimension getMaxSize() similar to getMinSize
using C.getMaxSize() public void
invalidate() sizesAreValidfalse if
(myContainer!null) myContainer.invalidate()
29Spatial arrangements
- Simple Stacks not enough to provide layouts
- Need some more control over spacing between
components - We use spreaders and spacers to do this
- In java this is the role of the Box.Filler class
- Special widgets that are not drawn
- Invisible
- Have min, desired and max sizes
- Spacer
- min, desired and max sizes set to same value
ensuring fixed space between widgets - Spreader
- Small min and desired but large max making
spacing widgets apart - Use of spreaders and spacer is effective but not
that intuitive for new designers.
30Layout Managers
- Separate objects that handle a particular style
of layout - Desired layout for containers is a property of
the container
31public interface LayoutManager public
Dimension getMinSize(Widget containerWidget)
public Dimension getDesiredSize(Widget
containerWidget) public Dimension
getMaxSize(Widget containerWidget) public void
doLayout(Rectangle newBounds, Widget
containerWidget) public class Widget .
. . other methods and fields . . . private
Dimension minSize private Dimension
desiredSize private Dimension maxSize
private boolean sizesAreValid private
LayoutManager myLayout public LayoutManager
getLayoutManager() return myLayout public
void setLayoutManager(LayoutManager newLayout )
myLayoutnewLayout invalidate()
public Dimension getMinSize() if
(sizesAreValid) return minSize
minSizemyLayout.getMinSize(this)
sizesAreValidtrue public Dimension
getDesiredSize() . . . similar to getMinSize()
. . . public Dimension getMaxSize() . . .
similar to getMinSize() . . . public void
doLayout(Rectangle newBounds)
myLayout.doLayout(newBounds,this)
32Summary
- Introduced the importance of layout and placement
- Discussed a series of techniques for layout
- Outlined the broad approach of layout managers.
- Next Examples of the use of Swing Layouts in
practice - Read through the swing tutorials.