Elastics and Page Layout

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:

Aligning Graphical Objects

You may think of aligning graphical objects from two perspectives.
The following sections provide examples using both perspectives.

Specifying Children's Outside Origins

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}
}

Specifying Parent's Alignment Property

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:
Graphichalignvalign
FrameYY
HBoxNY
VBoxYN
TableYY
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}
}

Stretching and Compressing Objects

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.

Elastic Basics

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.

Elastic Parameters

An elastic is characterized by six parameters:
A few situations in which you may think about elastic parameters are:
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.

Origin Elastic

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.

About Stretch Order

The following example shows an HBox containing two graphical children. Each of their widths is an explicit call to make-elastic. Note that:

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
                  }
        }
    }
}

About Stretchiness

The following example shows an HBox containing two graphical children. Each of their widths is an explicit call to make-elastic. Note that:

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
                  }
        }
    }
}

Converting Dimensions to Elastics

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:

Converting Dimensions to OriginElastics

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.

Page Layout Techniques that use Elastics

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.

Setting the width/height Option

width and height are most commonly set to:
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: 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. 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.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
            }
        }
    }
}

Using Fill as Padding

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.}
    }
}

Setting Containers' hstretch? and vstretch? Options

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:

Using OverlayBox

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.

Example with Multiple Alignment Techniques

The example in this section shows alignment by using these alignment options and elastics:
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}
    }
}

Elastics in a Page

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.

Table Elastics

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}}
        }
    }
}

Troubleshooting

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.
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"}
}

The Layout System

Layout Negotiation

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.
Height-first layout negotiation is entirely analogous to width-first layout negotiation except that the roles of height and width are interchanged. Accordingly, the first phase begins with Graphic.get-height-preference, the second phase begins with Graphic.constrain-height. The third phase takes place with Graphic.set-size.
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.