Arranging FireMonkey Controls
Go Up to FireMonkey Application Design
A FireMonkey application presents one or more controls in a form. The form can contain 2D content, 3D content, or a mixture of both.
Using the FireMonkey Coordinate System
In the FireMonkey Form Designer, the origin of the coordinate system is the top-left, extending to the bottom-right. Coordinates are expressed as single-precision floating-point numbers. All supported platforms use square pixels. One coordinate unit usually maps to one pixel, with some distinct exceptions.
- The Position property of a 2D control is a TPosition with X and Y properties. The separate Width and Height properties represent its size.
- 3D objects use a TPosition3D with an additional Z property, with positive values pointing into the screen (X goes to the left and Y points down, so this follows the "right-hand rule"); and a Depth property. Together, the position and size define one kind of bounding box that describes a control: its content box.
FireMonkey Controls Have Owners, Parents, and Children
FireMonkey allows any control to be the parent of another.
The form usually owns all the controls in it, and controls laid out in the Form Designer follow this convention. The owner is the first and only argument for the common Create constructor:
When creating components through code, if the control is intended to persist through the remaining lifetime of the form, specify the form as the owner. The form should be readily available either as Self or as the Owner of an existing control. The owner is responsible for disposing of the control when it is disposed itself.
For components that are transient, pass
nil as the owner. The code is then responsible for disposing of the component when it is finished with it. Best practices dictate that a try/finally block is used to ensure the component is disposed of, even if there is an exception.
In order for the control to appear in the form, ownership is not enough. It must also be placed in the component tree, either as the direct child of the form, or somewhere further down the tree. Controls laid out in the Form Designer do this automatically, and the component tree is shown in the Structure View. When creating controls through code, set the Parent property to the form or the appropriate parent control.
The Position of a child is relative to its Parent. If the coordinates are zero, the child starts at the same top-left as the parent.
Parentage is not restricted to container-like controls. Also, the ClipChildren property defaults to False (if True, it would not allow drawing of children outside control's content box). This enables ad-hoc collections of related controls without requiring a formal container. For example, a TLabel can be a child of the TEdit it describes. The label can have a negative position, placing it above or before the control. Moving the TEdit moves both together. TLayout can be used as an otherwise featureless container to arrange other controls.
In addition to a shared coordinate space, child objects share other attributes like visibility, opacity, rotation, and scale. Changing these attributes of the parent affects all the children in that sub-tree.
Aligning with Margins and Padding
A control's Align property determines its participation in automatic positioning and/or sizing along its parent's four sides or center, both initially and as the parent is resized. It defaults to None, so that no such automatic calculations are perfomed: the control stays where it is. The property is an enum of type TAlignLayout, with over a dozen other possible values. Most of them cause the calculation to include two values for automatic alignment: the parent's Padding and the control's Margins.
Padding sets aside space on the interior of the parent's content box. For example, if the parent's Top and Left padding values are both 10, then a component that is automatically positioned in the top-left has its position set to 10,10.
More accurately, what is automatically positioned is not the control's content box, but rather its layout box. The difference between the two is the control's Margins, if any. Margins set aside space on the exterior of the control's content box. As the size of Margins increases, the size of the layout box stays the same, and the content box shrinks if it is constrained. Going back to the 10,10 example, if the Top and Left margins are both 5, then the position of the control is 15,15.
Note: The role of margins and padding in FireMonkey respects the CSS Box Model.
Margins ensure separation between controls automatically positioned by a parent, and Padding ensures space between those controls and the parent's edge. This is true for positive values in margins and padding; negative values are also allowed. Negative margins place children outside the parent's content box, which are still rendered if its ClipChildren property is False. Negative padding places a control's content box outside its computed layout box.
Anchors are needed when a control must maintain its position at a certain distance from the edges of its parent, or must stretch while maintaining the original distance between its edges and the edges of its parent. Anchored controls stick to the sides of containers and stretch, if so specified. Using a combination of anchors and nested containers you can create intelligent stretchable layouts in FireMonkey. To set the anchors of a control, use the Anchors property. An anchored control keeps constant the design-time specified distance between it and the edges of its parent, when the parent resizes at run time. At design time, setting the Anchors property has no visual effect over the form.
A control can be anchored to none, one, or more than one edge. If a control is anchored to opposite edges at the same time (Anchors=[akLeft, akRight] or Anchors=[akTop,akBottom]), the control stretches either horizontally or vertically to maintain constant the distance to the left and right, or top and bottom edges of its parent. A control can be anchored to all 4 edges of its parent; in this case, it stretches in all directions. By default, a control is anchored to the top and left edges of its container (Anchors=[akTop,akleft]).
Note: Do not anchor the children of a scroll layout (TScrollBox, TVertScrollBox, and so on) to the right and bottom edges. If the Anchors property of a scroll layout child is set to akBottom, akRight, or both, the child will continue to stretch to keep constant the distance to the layout edges when the layout content size is being calculated. The children of a scroll layout should be anchored only to the left and top edges.
Scaling and Rotating
Two other commonly available attributes affect a control's final rendered location: scale and rotation.
Note: Scale and rotation do not alter a control's position or size properties. This is reflected in the Form Designer: a selected object's eight grip dots (four corners and four sides) mark the actual content box, set manually or computed through layout, before applying scale and rotation.
A control's Scale property is represented by an instance of the same type as its Position: TPosition for 2D objects and TPosition3D for 3D objects. Its X, Y, and Z values default to 1, meaning that the object is unscaled in all dimensions. The scale value is a simple multiplier on each axis. Values larger than one will stretch along that axis. Values less than one but greater than zero will shrink or squish along that axis. Scaling any axis by zero will cause the control to disappear. Uniform scaling requires the same value in all axes.
2D scaling is always anchored from the control's origin, the top-left of its content box. Negative scaling pivots on that origin point. For example, a negative X scale will cause the control to render down and to the left, flipping it on the content box's left edge. 3D scaling is from the object's center.
In 2D rotation, the pivot is adjustable. The RotationCenter property is also a TPosition, but the value is in unit coordinates: 0,0 is the top-left of the control and 1,1 is the bottom-right. It defaults to the center of the control: 0.5,0.5. The aspect ratio of the content box does not matter. On that pivot point, the RotationAngle is in degrees, clockwise.
In 3D, rotation is always from the center, with the RotationAngle a TPosition3D, specifying degrees on the X, Y, and Z axes. Rotation also follows the right-hand rule; for example, with X and Y rotation zero, the Z axis points into the screen, and positive rotation on the Z axis rotates clockwise.
In 2D, scaling occurs before rotation, which matters because scaling is from the origin and the rotation is adjustable. In 3D, both occur from the center, so the order does not matter.
Using FireMonkey Layouts
FireMonkey layouts are containers to be used to group other controls, to arrange and manipulate them as a whole. FireMonkey layouts are nonvisual components. Using layouts along with one or more of the arranging ways presented above can result in complex interfaces without using complex mathematical calculations.
There are layouts with predefined arrangements for child controls:
- TFlowLayout arranges its child controls as if they were words in a paragraph.
- TGridLayout arranges child controls in a grid of equally sized cells. All controls are automatically resized to fit cells. When using this kind of layout, the alignment, positions, and anchors properties of the controls are automatically set. Changing these properties explicitly is ignored. The size of the controls is affected and set automatically when the TGridLayout is used.
- TGridPanelLayout arranges child controls in a grid panel. Each control is placed within a cell on a grid, but you can manually resize controls inside cells. You can specify that a control can span on several cells.
FireMonkey provides several scroll box layouts to represent scrolling areas containing child controls.
Scroll boxes are used to group several controls (such as buttons, list boxes, edit boxes, radio buttons, and so on) under the same scrollable parent:
- TScrollBox--Represents a scrolling area in a form.
- THorzScrollBox--Represents a TScrollBox restricted to horizontal scrolling.
- TVertScrollBox--Represents a TScrollBox restricted to vertical scrolling.
- TFramedScrollBox--Represents a TScrollBox having an opaque background with the surrounding frame.
- TFramedVertScrollBox--Represents a TVertScrollBox having an opaque background with the surrounding frame.
Using scroll boxes, a form can contain a lot of controls organized in a scrollable area in order to occupy less space on a form. Another use of scroll boxes is to create multiple scrolling areas (views) in a form. Views are common in commercial word-processor, spreadsheet, and project management applications.
Under iOS, Mac OS, and Android, a scroll view responds to the speed and direction of gestures to reveal content in a way that feels natural to people. FireMonkey provides the InertialMovement unit emulating such smooth inertial moving of a scroll view under Windows.