When you create an applet written in the Curl® language,
the Curl® GUI Toolkit's layout system takes
care of the page layout. That is, it automatically aligns,
stretches, and compresses graphical objects where expected.
However, in some cases, you may want to fine tune the layout to
suit your application's needs.
Because the Curl language relies on nested graphical containers to place
objects, it is helpful to understand the relationship between
objects and their containers as well as the relationship
between sibling objects within
the same container.
The
Graphical Containers chapter
describes the various containers you can use to create a nested
graphical hierarchy.
This chapter describes the basic techniques for
aligning, stretching, and compressing graphical objects. It
consists of the following sections:
You may think of aligning graphical objects from two perspectives.
- By placing the responsibility on the children.
Each container parent has its own
way of displaying its children, usually using the
children's outside origin. You can change
the children's outside origin to accommodate the parent's
display algorithm.
- By placing the responsibility on the parent.
Most container objects use the halign or
valign options to provide an additional
way of performing alignment.
The following sections provide examples using both perspectives.
The bounds on each graphical object is defined relative to its own
origin, called its inside origin. The inside origin is
often located at the graphic's geometric center or on one of
its corners, such as the upper-left or lower-left corner.Within a graphical hierarchy of nested objects, each graphical
container has its own way of ordering its graphical children, and
uses what is called the children's outside origin. The
location of a Graphic's outside origin is movable. That is,
it is not part of the Graphic's definition. Its default value,
however, is its inside origin.For example, HBox places its graphical children
in a horizontal order, from left to right, ensuring that
the vertical component of their outside origins are aligned.
You could guess from the example that the inside origins
of the RectangleGraphic, the TextFlowBox,
and the Fill are lower-left, base line of text,
and center respectively because they are the default
values for their outside origins.The simplest way to realign the objects would be to set
their outside origins to more appropriate values.
To do so, set each graphical object's vorigin
option. vorigin specifies the vertical component
of the Graphic's outside origin.In the following example, you can simply uncomment
the option specifications to see the result.
| Example:
Setting vorigin |
 |
{HBox
border-width=3pt,
border-color="green",
{RectangleGraphic
||vorigin="center",
width=1cm,
height=1cm
},
{TextFlowBox
||vorigin="center",
border-width=1pt,
border-color="black",
"Quick Kangaroo"
},
{Fill
||vorigin="center",
background="purple",
width=2cm,
height=1.2cm}
}
| |
Another way to align objects is to deal with it from the parent's
point of view. Different graphical containers have different
ways of performing top-down alignment using halign and/or
valign. If you use these options, the graphical origins of the
children are ignored.These graphical containers include the following: The following example shows the HBox aligning the upper
bounds of its children as valign is set to "top".
In using this option, it does not matter where each of the
graphical children's outside origins are located because those
values are not used.
| Example:
Setting valign |
 |
{HBox
|| set the parent's alignment option
valign="top",
border-width=3pt,
border-color="green",
{RectangleGraphic
width=1cm,
height=1cm
},
{TextFlowBox
border-width=1pt,
border-color="black",
"Quick Kangaroo"
},
{Fill
background="purple",
width=2cm,
height=1.2cm}
}
| |
As you build your applet, your design will probably use nested graphical
objects. In most cases, the GUI Toolkit automatically performs the
layout that you want. That is, objects stretch and compress relative to
their parents and siblings as expected. In some cases,
where neither stretching nor compressing is appropriate, a child may
be truncated, scrollbars may appear, or, in the case of TextFlowBox,
text will wrap.However, there are probably times when you want to specify the width
and height behaviors of the objects, rather than use the default
behaviors. Then it is helpful to understand the basic principles of
elastics, as described in the next section.
Although you might think of the width and height of a graphical
object as a set distance, such as 8pt or 1in, underlying
the widths and heights of all graphical objects are Elastics. The
fact that elastics exist should, in most cases, be invisible to you.As layout occurs, there is a stage called layout negotiation, during
which, from the top of the graphical hierarchy each graphical object
is polled for its size preferences (usually width preference first),
which it provides its parent.The width and height options of a graphical object represent
one aspect of the Graphic's width and height preference.
Technically, they are of type
Dimension, but take various types, internally converting them to
a Dimension objects.
The Dimension is then internally interpreted as an Elastic;
more specifically, an OriginElastic.Hence, an elastic defines the behavior of a
graphic in relation to its parent container, its siblings, and its
graphical children, if any.To summarize this complex sequence rather simplistically, the result of
layout negotiation is that each object is assigned a size which may
or may not coincide with the object's preference, which is stated
in the form of an elastic.
An elastic is characterized by six parameters:
A few situations in which you may think about elastic parameters are:
- A child stretches because its preferred size is less than the
parent's layout size.
- A child is compressed (by its parent) because its preferred
size is greater than its parent's layout size and it has a
high compress order.
- A child X stretches more than child Y in a "spacious" parent
object if:
- Child X and Child Y have the same stretch order and
Child X's stretchiness value is larger than that of
Child Y.
- Child X has a greater stretch order than Child Y
When you set the width option of a graphic to a Distance
such as 3in, you are, in effect, specifying a rigid elastic,
whose preferred size is 3in. The stretch order and compress order values
are very low.
The following sections describe stretch order and stretchiness
with examples.
Compress order and compressibility are similar.
The examples create
Elastic objects by calling
make-elastic, supplying the parameters described
above as arguments.
Although this chapter uses the term "elastic" to describe
a Graphic's width and height preferences, these preferences are
implemented as
OriginElastics. An
OriginElastic is
made up of two
Elastic components, separated by the Graphic's
inside origin.
For a Graphic's width preference, the first component
is the left elastic; the second component is the right elastic. Similarly, for
each Graphic's height preference, the first component is the elastic
above the inside origin, and the second component is the elastic
below the inside origin.
As mentioned earlier, the inside origin of a graphic is
often located at one of its corners or its geometric center. If
it is on one of its corners, this means
that one of the components is 0, and all the elastic parameters
that are set on the option apply to only one component (for the
width as well as the height). In this case (which applies to many graphical
objects), when the width is "stretchy", this means that the graphical
object's width is stretchy to one side of its inside origin.
If the Graphic's inside origin is not located on one of
its corners, the graphic could stretch/compress unevenly on each side
of the inside origin, depending on the elastics of both the first
and second components.
The following example shows an
HBox containing
two graphical children. Each of their widths is an
explicit call to
make-elastic. Note that:
- The children of the HBox
have the different stretch orders.
- They have the same stretchiness coefficient, but these
values are not taken into account because their stretch orders
are different. Essentially, the CheckButton has stretched
the CommandButton out of visual existence.
| Example:
Stretch Order Differences |
 |
{value
{HBox
width={make-elastic
preferred-size=10cm},
{CommandButton
width={make-elastic
stretch-order=30,
stretchiness=100
}
},
{CheckButton
background="lime",
width={make-elastic
stretch-order=40,
stretchiness=400
}
}
}
}
| |
The following example shows an
HBox containing
two graphical children. Each of their widths is an
explicit call to
make-elastic. Note that:
- The children of the HBox have the same
stretch order. Therefore, their stretchiness
coefficients are taken into account to determine how much
(proportionately) each will occupy the HBox.
- Since the left button has a stretchiness of 100 and the
right button has a stretchiness of 400, the left button
occupies 20% of the available width.
| Example:
Explicitly Settings Widths to Elastics |
 |
{value
{HBox
width={make-elastic
preferred-size=10cm},
{CommandButton
width={make-elastic
stretch-order=30,
stretchiness=100
}
},
{CheckButton background="lime",
width={make-elastic
stretch-order=30,
stretchiness=400
}
}
}
}
| |
When a Dimension
is supplied as an operand to an Elastic operation
such as Elastic.set or Elastic.add,
it is translated into the corresponding Elastic
according to the following rules: - A Dimension that is a Distance
d such as 2.5cm or 5in
corresponds to an elastic equal to
{make-elastic
minimum-size=d,
preferred-size=d,
stretch-order=rigid-stretch-order}
which is usually the Elastic you want if you are
trying to describe a rigid object of size d. - A non-null Dimension that is neither a Distance nor an Elastic is first converted to a
Distance using the procedure any-to-Distance before conversion to an Elastic.
In particular, a nonnegative number n with no
unit specified is assumed to represent a distance in units
of points, so it is replaced by the Distance
npt before conversion to an Elastic.
Note: There are restrictions on the nature of the
quantities that are appropriate to specify in this
context. These quantities should be expressed in
resolution-independent units, such as cm and in. They should not be specified using PixelDistances, FloatPixelDistances,
EmDistances, or FloatEmDistances.
If a PixelDistance or an EmDistance is
specified, an error will not be reported, but the quantity
will be converted to resolution-independent units using a
default DisplayContext that may differ from the
DisplayContext that will be used when the graphical
object described by the Dimension is actually
rendered to a display device. - A Dimension that is null corresponds to an
elastic equal to unstretchable-zero-length-elastic.
- If a Dimension is an Elastic, then it is
just used directly.
When a Dimension
is supplied as an operand to an operation,
such as OriginElastic.set, that converts its
Dimension argument to an OriginElastic,
the following translation rules are applied: To convert a Dimension explicitly to an OriginElastic following the above rules, use dimension-to-origin-elastic.
The Curl® GUI Toolkit provides various ways in which you can
adjust the underlying elastics of objects, described in the following
sections:
The following sections describe how elastics are used and
show an example using some of the techniques.
width and height are most commonly set to:
- Distance objects,
- Elastic objects (such as calling make-elastic)
- procedures that take a Graphic and return
an OriginElastic (such as add-stretch).
When you set the width or height option of a graphical
object, the value, if it is not an elastic, is internally converted
to one.
To set an object's width/height to an elastic directly,
you can set it to one of the following procedures:
Other ways of using elastic include using these procedures:
- vcenter: Wraps a VBox around the Graphic you
specify, placing an object above and below it, where the objects' height
stretch orders are the value you provide as the second argument to
vcenter (the default value is 30).
- hcenter: Wraps an HBox around the graphic you
specify, placing an object to the left and right of it,
where the objects' width stretch orders are the value you provide
as the second argument to hcenter (the default value is 30).
When text is displayed, it is wrapped within a
TextFlowBox for placement in the graphical hierarchy.
Some elastic characteristics of the TextFlowBox are worth mentioning
because they accommodate the flow of text.
- Height-wise, TextFlowBox always gives objects their
preferred sizes; it never stretches or compresses objects
vertically.
- Width-wise, TextFlowBox
will always stretch to accommodate
its text to the limits of its own container, after which
the text will wrap. Also, TextFlowBox has a higher compress
order than most other graphical objects, causing it to be the
object that is compressed to its minimum size when width
space is limited.
This information is especially useful when you are doing
top-level page layout, since the top-level object in a browser is a document,
and each of the available document styles is a different type of object.
DefaultDocument and TocDocument are TextFlowBoxes
wrapped in a ScrollBox. PlainDocument is not a
TextFlowBox. See Elastics in a Page
for more information.- text-width-display wraps the graphic you supply as
argument in an object that stretches to fill the width of the
nearest enclosing TextFlowBox.
- center wraps the graphic or text
(actually its TextFlowBox)
in an object that places a stretchy object on either side, thereby
centering the graphic (or text) within its nearest enclosing
TextFlowBox.
- right-justify wraps the graphic or text
(actually its TextFlowBox)
in an object that places a stretchy object on the left side, thereby
forcing the graphic (or text) to the right side of its nearest enclosing
TextFlowBox.
The following example shows the use of text-width-display.
| Example:
Using text-width-display |
 |
{paragraph
paragraph-left-indent=1cm,
paragraph-right-indent=1cm,
{paragraph
font-weight="bold",
paragraph-justify="center",
Regional Championship}
{text-width-display
{HBox
{paragraph color="purple",
{underline Purple Team}{br}
Violet Schneider{br}
Lila Lee
},
{Fill},
{paragraph color="green",
{underline Green Team}{br}
Nina Gruengarten{br}
Ching Yi
}
}
}
}
| |
Fill is often used as padding because, its stretch order
and compress order values are, by default, very high (40).You may want to use Fill as space fillers within a
container whose objects are intended to be spread out to the
bounds of the container. This allows the "spaces" rather than
the objects to stretch, if that is the intention, since Fill's
default stretch order is higher than the default stretch
orders of most other graphical objects.As with other graphical objects, you can explicitly set the
width and height of Fill to elastics.
| Example:
Using Fill with text-width-display |
 |
{text-width-display
{HBox background={LinearGradientFillPattern
{Fraction2d 0.0, 0.0},
{Fraction2d 1.0, 0.0},
{Spectrum.from-endpoints
"#80b4b3",
"white"
}
},
{text Thank you for using Curl IDE},
{Fill} ,
{link href={url "http://www.curl.com/"}, Curl, Inc.}
}
}
| |
Some graphical containers have the local option hstretch?
or vstretch?. They cause their children to stretch if the
option is set to true.These options' defaults are false.
When the option is false, only those children whose stretch order is
greater than 25 are stretched. See Frame.hstretch? for
an example.These container class are:
You may use OverlayBox if your graphical hierarchy contains objects
that should be displayed only one at a time. OverlayBox
stretches its children to its own bounds so that, by default,
only the topmost (nontransparent) object is visible.
The example in this section shows alignment by using these
alignment options and elastics:
- valign
- make-elastic
- hcenter
- text-width-display
- Fill
| Example:
Using Elastics Through Options and Procedures |
 |
{define-proc {control-box}:VBox
{return
{spaced-vbox
|| use make-elastic to set a minimum size
width={make-elastic
minimum-size=2in,
preferred-size=2in,
stretch-order=30,
compress-order=30
},
margin=0.5cm,
border-width=1pt,
border-color="black",
{text How do you feel about sharks?},
{RadioFrame
{VBox
{RadioButton label="I love sharks."},
{RadioButton label="I am terrified of sharks."},
{RadioButton label="Other"}
}
},
|| use hcenter for padding on both sides of the "Vote" button
{hcenter {CommandButton label="Vote"}}
}
}
}
{HBox
|| use valign to vertically align the 2 children by their centers
valign="center",
margin=0.5cm,
background="#aaaaff",
{TextFlowBox margin=0.5cm,
{center {bold Warning to Tourists}},
"Do not pat the heads of the sharks. They are not pets.
This is not a petting zoo.
Do not stand on the dead whales. They are being consumed
by the sharks. Thank you for your attention."
},
{control-box}
}
|| text-width-display stretches the sum of the objects to the
|| width of the enclosing TextFlowBox (in DefaultDocument or View)
{text-width-display
{HBox
|| use valign to vertically align the 3 children by their centers
valign="center",
margin=0.5cm,
background="#aaaaff",
{Frame margin=0.5cm,
{image
source={url "../../default/images/generic.gif"},
width=2in, height=2in}
},
|| add Fill so that the control box does not get stretched
{Fill},
{control-box}
}
}
| |
Every applet displayed in a browser, is contained in a document.
There are three document styles. Use document-style to specify
one of the three available document styles. If you do not explicitly call
this procedure, DefaultDocument is used, by default. The stretch/compress behavior of your applet therefore differs
depending on which document you are using. If your applet is
based on text, it might be preferable for you to use a
the DefaultDocument or the TocDocument because
the TextFlowBox gives the text its preferred height,
and the all-enclosing ScrollBox provides
scrollbars when needed.
If you are creating an applet that, at the highest
level, is an object that has to exactly fill the page
vertically, you must ensure that no
ancestor of your object is a TextFlowBox. Thus,
you should not use DefaultDocument or TocDocument.
Instead, PlainDocument may give you the behavior you
need, since it is a Frame.
Click
plain-document.curl
for an example of how you can center a graphic vertically by
using
Fills and center it horizontally by setting
the
VBox.halign option.
Click
default-document.curl
for an example of how you can center a graphic horizontally in a
DefaultDocument. Notice that using Fills in an attempt to center it
vertically does not work. You should use a PlainDocument if it is necessary
to center the graphic vertically.
When using Tables, you can control the elasticity of the general
width, as well as individual columns of the table.column-prototype and row-prototype can
be used to set options, including width.You may add a *-prototype just to set options (such as width).Consider the following examples:
| Example:
Setting Table and Column Widths to Elastics |
 |
{let t:Table=
{Table
|| Ensure that table's width stretches when the
|| dialog is stretched by the user.
width={add-stretch},
background="#8888aa",
{column-prototype
|| Prevents the first column from stretching
|| when the table stretches.
width={make-elastic
minimum-size=1in,
preferred-size=1in,
stretch-order=10,
compress-order=10,
compressibility=50
},
{cell-prototype {text UserID:}},
{cell-prototype {TextField}},
{cell-prototype colspan=2,
{CommandButton
|| Stretches the "Need Hint" CommandButton
|| to the left and right of its inside origin.
width={add-stretch before?=true,
after?=true},
label="Need Hint"}
}
},
{column-prototype
|| Prevents the 2nd column from stretching
|| when the table stretches.
width={make-elastic
minimum-size=1in,
preferred-size=1in,
stretch-order=10
},
{cell-prototype {text Password:}},
{cell-prototype {PasswordField}},
{skip}
},
{column-prototype
|| Without resizing, the width of the 3rd column is 2in
width={make-elastic minimum-size=2in},
{cell-prototype {text From:}},
{cell-prototype {TextField}}
},
{column-prototype
|| Without resizing, the width of the 4th column is 2in
width={make-elastic minimum-size=2in},
{cell-prototype {text To:}},
{cell-prototype {TextField}},
{cell-prototype
|| Right align the "Search" button within
|| its cell.
{text-width-display
{HBox {Fill}, {CommandButton label="Search"}}
}
}
}
}
}
{CommandButton
label="stretchy table in resizable dialog",
{on Action do
{let dialog:Dialog=
{Dialog
t
}
}
{dialog.show style="resizable", title="Ticket Search"}
}
}
| |
| Example:
Placing Table in text-width-display |
 |
{text-width-display
{Table columns=2,
width={make-elastic minimum-size=2in, stretch-order=40},
background="#8888aa",
{row-prototype
{cell-prototype
colspan=2,
{text Show me the cheapest round trip ticket for:}
}
},
{row-prototype
{cell-prototype {text From:}},
{cell-prototype {TextField}}
},
{row-prototype
{cell-prototype {text To:}},
{cell-prototype {TextField}}
}
}
}
| |
As mentioned in the beginning of this chapter, elastics
form the basis of the width and height dimensions of graphical objects.
And because of them, when you nest objects in a graphical hierarchy,
the objects usually stretch or get compressed in a way that you and
your user would expect.If the default stretch/compress behavior do not coincide with your
design, use the following techniques to adjust them.Other troubleshooting scenarios will be presented in a future release.
Child does not stretch to the bounds of the
Parent.
- If you know the preferred size of the parent, set the child
to the same size.
- Set the parent's hstretch? or vstretch? option
to true.
- Explicitly set the compress order of the child to a lower
value than the parent's.
- Explicitly set the compress order of the parent to a higher
value than the child's.
The following example shows a VBox containing
Table and a CommandButton.
The VBox stretches just enough to accommodate
its children's sizes. Uncomment the comments to:
| Example:
Stretching A Child to Parent's Bounds |
 |
{VBox
||width=2in,
||hstretch?=true,
spacing=1.5cm,
border-width=1pt,
border-color="navy",
{Table columns=2,
{text Name:},
{TextField},
{text State:},
{DropdownList
"MA",
"RI",
"NH"}
},
{CommandButton
label="Reset all entries"}
}
| |
Graphical layouts in Curl applets are determined by a process of
layout negotiation
that involves
Graphic objects in a graphical hierarchy.
Layout negotiation begins by polling each object for its
width and height preferences, and concludes with the assignment of
widths and heights to each Graphic. It proceeds either
width-first or height-first, although width-first is the more
commonly used order.
Width-first layout negotiation usually begins when
Graphic.get-width-preference is called on
on each
Graphic in a graphic hierarchy, polling each graphic for
what it would like its width to be.
After the width preferences of all
Graphics in
the graphic hierarchy have been collected, the widths to be assigned to
the objects in the graphic hierarchy are computed for use in the second
phase of layout negotiation, which is a poll for what each graphic would like
its height to be if it were given its width preference (from the first phase).
This second phase occurs when
Graphic.constrain-width is called on all
Graphics in the graphic
hierarchy. The resulting height preferences are then used to
calculate the heights to be assigned to the
Graphics in the final
phase, which is when the width and height of each
Graphic are assigned. This phase occurs when
Graphic.set-size
is called on each of the
Graphics.
Some layout negotiations are abbreviated and skip the first phase.
However, the second and third phases, in which each Graphic
is informed about its new size, must always occur.
Copyright © 1998-2007 Sumisho Computer Systems Corp.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of Sumisho
Computer Systems Corp. that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of Sumisho Computer Systems Corp.