Definition Building Blocks
GUIKit definitions are defined as a hierarchy of widgets, whether they are defined in the Wolfram Language expression format or in the XML format, GUIKitXML. A single self-contained user interface definition should always begin with an outermost user interface widget—either an instance of a window or frame. The GUIKit framework also optionally provides an appropriate top-level window wrapper if the root interface definition widget is at least a user interface class that can live inside a window or frame, so you are not actually required to always use a top-level frame or window widget. This is convenient because it allows you to write reusable complex widgets by designing them with the root widget as Widget["Panel"]. This could be run by itself or reused as content in other developers' window definitions. Examples of reusing panel-designed higher-level widgets exist in subsequent documentation sections.
A GUIKit definition expression consists of a hierarchy of widget expressions.
Some of the building blocks that make up a user interface definition include the following.
Request the creation of a new widget instance.
Reference an existing widget previously registered under myName (using the Name option).
Wolfram Language scripting code blocks contain arbitrary Wolfram Language programming code executed within the context of the user interface instance with access to the widget objects defined in the user interface.
Event binding provides triggers for Wolfram Language code to execute when certain events happen within the user interface.
Set or get the state and values of live widgets in the interface.
If the widget definition requires arguments when it is created, you can set these with InitialArguments.
There is an equivalent set of XML elements that define a user interface definition analogous to the previous Wolfram Language building block expressions.
Widget Layout
GUIKit definitions are defined as a hierarchy (a "tree" expression) of widgets, but at the same time the definition's nesting structure and list depth also determine how the widgets will visually lay themselves out at runtime. The goal of the layout rules is to make the most common and useful forms easy to represent in the definition, and at the same time provide the necessary optional rules to allow for finer control over the layout. Additionally, the layout rules follow familiar Wolfram Language list structures and do not require knowledge of the underlying Java rendering implementation layout APIs. This high-level abstraction of determining layout provides a simple, immediate, and intuitive layout as well as the necessary dynamic layout resizing features that all modern user interface dialogs are expected to adhere to when resized.
You can achieve most of the desired layout appearance of interface definitions by simply nesting widget expressions within other widget expressions and choosing the desired level of {…} nesting to determine the resulting automatic vertical or horizontal grouping.
The following basic heuristics are followed in the GUIKit widget layout system.
- The outermost (top-level) list of widgets, children in Widget[comp,children_List], defaults to using a vertical (column) layout.
- Each type of widget has its own default horizontal and vertical stretching characteristics. For example, text fields automatically stretch in the horizontal direction but not vertically.
Here are some examples of these default layout features.
By default, a list of children widgets is laid out vertically in a column starting from the left edge of the panel.
Nesting lists within a vertical layout creates horizontal layouts of these internal groupings.
Further nesting alternates with vertical and horizontal layout nesting.
Here you see how certain types of widgets default to maximize their sizes over other types of widgets, which adheres to typical user expectations.
Note that with the new size of the window, the defaults cause the text field to resize horizontally, while the text area expands in both directions.
When simple nesting of widgets is not enough to achieve the desired layout or resizing characteristics, there are a number of additional Widget interface definition expressions and options that can aid in tweaking the results. Namely, the additional definition expressions include WidgetGroup, WidgetFill, WidgetSpace, WidgetAlign, and the Widget and WidgetGroup options that can choose alternate default values for features, such as "Stretching", "Alignment", "Grouping", "Spacing", and "Border" elements.
These additional expressions and options are introduced through the following visual examples.
WidgetGroup
When the default layout alignment based on the depth of the list nesting is not appropriate, you can use the form WidgetGroup[{…},WidgetLayout->Row] or WidgetGroup[{…},WidgetLayout->Column] to explicitly specify the next alignment.
WidgetGroup[{…},WidgetLayout->Row] | |
explicitly force a horizontal layout of the child widgets | |
WidgetGroup[{…},WidgetLayout ->Column] | |
explicitly force a vertical layout of the child widgets |
The basic forms of WidgetGroup usage.
WidgetGroup[{group1,group2,…}, WidgetLayout->{"Grouping"->{Tabs,{"Tab Label1","Tab Label2",…}}}] | |
place the labeled tabs at the top of the generated tab pane | |
WidgetGroup[{group1,group2,…}, WidgetLayout->{"Grouping"->{Tabs,Bottom,"Tab Label1","Tab Label2",…}}}] | |
place the labeled tabs at the bottom |
Groups displayed in tabbed panes.
WidgetGroup[{group1,group2},WidgetLayout->Split,Name->"mySplitPane"] | |
place a resizable split bar between the two groups and optionally name the generated split pane as "mySplitPane" | |
WidgetGroup[{group1,group2},WidgetLayout->{"Grouping"->{Split,Vertical}}] | |
use a vertical split bar | |
WidgetGroup[{group1,group2},WidgetLayout->{"Grouping"->{Split,Horizontal}}] | |
use a horizontal split bar |
Groups displayed in split panes.
WidgetGroup[{{…},{…},…},WidgetLayout->Grid] | |
use a grid of evenly aligned elements as an alternative to multiple nested row and column lists (but this form has some limits as to resizing and staggering of child widget lists) |
You can override what would normally be a vertical layout by explicitly choosing a top-level row layout.
Nested lists still use the automatic alternating horizontal and vertical layouts and so the child list will now be a column.
You can explicitly replace any and all lists of widgets with WidgetGroup expressions.
Here you see an example of the Grid grouping.
Here is an example that generates content within a set of tabbed panes.
Each element of the tabbed WidgetGroup can be a list of widgets that populate one tab panel or a single widget whose contents will be placed within one pane.
Here is an example that uses a split pane and illustrates how you can optionally name the split pane so it can be manipulated using its properties at runtime.
If you resize the panel a little larger, you can see that you can programmatically move the divider position.
You can also call a method by taking a percentage value instead of using the integer property value.
WidgetFill
WidgetFill[] elements can be useful as "expanding springs" that force themselves to expand as much as possible along the layout direction they are placed and force other widgets within their group to be "pushed" to one side.
Perhaps the most common use of WidgetFill[] is to force a set of horizontal buttons to be right aligned in a dialog when resizing.
WidgetSpace
WidgetSpace[n] elements can be useful as simple spacers that create an explicit spacing element between widgets along the layout direction they are placed.
Here you add a little explicit spacing between the buttons.
WidgetAlign
WidgetAlign[] elements work as placeholder elements within groups of widget lists, and, for each one that exists within sibling lists, they will attempt to align themselves with other instances of WidgetAlign[], forcing a certain grid structure to exist without forcing the same number of widgets to exist between corresponding align markers. They function somewhat analogously to tab markers in word processing documents.
Another more advanced form of WidgetAlign is WidgetAlign[{"widgetName",After Before},Before After], which specifies that the very next widget following the WidgetAlign should align its beginning or end (Before or After) with another widget named "widgetName" along either its beginning or end. This form has somewhat limited use and requires properly named widget dependencies in an appropriate location to work well.
Here you place WidgetAlign[] marker elements where you would like sibling widget lists to align.
WidgetLayout Options
As you have seen earlier, WidgetLayout is an option for WidgetGroup that can specify the grouping directions for the containing widgets. Actually, this form of WidgetLayout->groupValue is a shorthand form for WidgetLayout->{"Grouping"->groupValue}. As it turns out, there are a number of different suboptions of WidgetLayout that provide different tweaks to layouts. The short form for grouping is available as it is very common to only use WidgetLayout to specify a grouping choice.
One other short form use of WidgetLayout is used with Widget and that is for the special case when you want to create a user interface widget, but you do not want to have the widget automatically added to the layout of its parent widget. In this case, you would turn off any widget layout work using WidgetLayout->None.
WidgetLayout->groupValue | shorthand form of WidgetLayout->{"Grouping"->groupValue} |
WidgetLayout->None | an option to Widget to turn off any automatic interface layouts with its parent widget |
Along with "Grouping", which allows all forms previously documented for the short form of WidgetLayout: Row, Column, Grid, Automatic, and None, the following other WidgetLayout suboptions exist.
"Grouping"
"Grouping"->groupValue takes on all grouping values documented for WidgetLayout short forms mentioned so far, and this suboption form must be used when combined with other WidgetLayout suboptions for a given WidgetGroup.
See examples of WidgetLayout->groupValue in the previous sections for examples of "Grouping" use.
"Stretching"
The "Stretching" suboption is supported both for Widget and WidgetGroup and is useful to specify an explicit horizontal and vertical stretching for a widget that may be different than the default stretching characteristics for a given widget type. Both a horizontal and vertical stretching value can be specified with allowable values: None, False, WidgetAlign, True, and Maximize.
"Stretching"->{horizontal,vertical} | override any default stretching characteristics for a given widget |
"Stretching"->None | keep a widget from stretching from its initial size |
"Stretching"->False | same as "Stretching"->None |
"Stretching"->True | allow stretching, but another widget with Maximize stretching may limit stretching if it exists |
"Stretching"->Maximize | allow stretching and also prioritize this widget's stretching over others that may simply allow stretching |
"Stretching"->WidgetAlign | allow stretching, but only to the extent needed to make a widget the same size as sibling widgets in its list |
Uses of "Stretching" suboption.
By default, labels do not stretch and text fields do, but you can override this with explicit "Stretching" values.
This is more apparent with a window resize.
"Alignment"
The "Alignment" suboption is supported both for Widget and WidgetGroup and can be used to specify both horizontal and vertical alignments. The default alignment value along the vertical axis is Left and can take values of Left, Center, or Right, and alignment on the horizontal axis defaults to Center, allowing values of Top, Center, or Bottom. Using Automatic for either of these axes will choose the appropriate default.
Uses of "Alignment" suboption.
It is very common to have a set of labels for text fields right align with the corresponding input fields.
"Border"
The "Border" suboption is a convenient way of building common border styles around groups of widgets. Three common types include borders specifying spacing only, a titled border with a name and ruled frame, and a simple line border with a thickness and optional color value.
"Border"->"title" | create a titled border style with a ruled outline |
"Border"->{{left,right},{top,bottom}} | create a border of spacing different for each of the four edges |
"Border"->n | create a spacing border of equal margins in all directions |
"Border"->{color,n} | create a framed line border of thickness n with color |
"Border"->{color,{{left,right},{top,bottom}}} | |
create a framed line border of different edge thicknesses with color | |
"Border"->{border1,border2,…} | create a compound border style with each of any possible border styles nested together |
Here is a simple titled border.
Note that some widgets, especially Widget["Panel"], can set a "border" property directly, and so a widget layout "Border" option can be specified directly with some widgets.
You can add a little inner spacing with a compound border of a title with a spacing border.
Here we have added a simple red border three points thick.
"Spacing"
The "Spacing"->n suboption allows a WidgetGroup to specify an overall spacing element to exist between all widgets in the group.
Here you have forced a spacing of 30 points between all widgets in the group.