Building GUIs - The Basics
The Context Node - A Recap
The term Context Node, often abbreviated to just context, applies in both the client and server environments, and its definition is the node that is yielded by the path $context. The same node usually results from the path $this, however there are circumstances when $this is not the context, for example in a loop body. The context node was introduced earlier, when discussing Inq's hierarchical data structures. When script is executing at a given context, the absolute path is given by the expression $path.
The context has greater visibility in the client because it is here that the node and its absolute node path (i.e. the path from the root node to the context node) originate. The context is important because it defines the relevant sub-division of the application for script to run in. Anything below the context can be accessed by a path of the form $this.something.useful. Any node reachable from $root remains in the node space (unless explicitly removed), so this data (and any sub-division of it) is the application's persistent state.
There is no path expression that navigates "up and out" of $this or $context, so the context defines the universe for a given script execution. There are ways to switch between contexts while a script runs. These require the use of function pointers (defined with the data type func) that encapsulate the context they were defined in. A func is used to create create an entry point between contexts, resulting in a structured route, rather than the unstructured use and brittle nature of any kind of path expression that could do the same.
In terms of the underlying GUI components in a client, things of significance that are created explicitly in the script become nodes beneath some parent when layout is performed. Inq will create other components to implement the layout, such as vertical and horizontal boxes or scroll panes, but these do not have to become Inq nodes as well. These components participate in the component hierarchy and exist to effect the desired graphical layout and groupings. A GUI component that is accessible as an Inq node has all its underlying Java Bean properties, and any additional ones implemented by its Inq node, accessible via the child path .properties.<property_name>. When script runs from a GUI event call-back, the context node is the nearest parent that has the property contextNode set to true. This could be the Inq node of the component that fired the event itself, though typically it is an ancestor node. The parent into which the Inq GUI nodes are placed during layout is often the one that defines itself as the context. These are typically one of
- a top level window;
- a container that is the immediate child of a tab (and so is the GUI hierarchy for that tab);
- the root of any component set that has been designed for reuse within the application.
Instance Hierarchies
From the foregoing we note that there are two instance hierarchies - the graphical component hierarchy and the Inq node hierarchy. The component hierarchy exists to achieve the desired GUI layout, while the Inq hierarchy exists to propagate a context to child nodes, as well as to simply contain the GUI items for general use.
The component hierarchy is not of major concern other than at layout time, when the component parent of a batch to be laid out is specified. The Inq hierarchy is generally flat for a set of GUI nodes inheriting a particular context node parent, so that paths are simple and of the (hungarian-style) form $this.bOK, an OK button, for example. As we will see in the section covering layout detail, the Inq and component roots for a particular case can be the same or different.
The Inq hierarchy is important when the tab container is used. If an Inq GUI node has a tab as its Inq parent then properties relating to its tab (such as the text and icon) become active.
Basic Steps of GUI Creation
Creating GUIs in Inq involves the following steps:
- create the components;
- set any required property values;
- tell the component what data it is rendering, that is attach the view to the model;
- ask Inq to lay out the components according to a specification for their placement, geometry constraints, bordering and other parameters;
- establish handler functions for component events of interest.
We use the the example scripted in C2F.inq to illustrate simple GUI building. We can run the example with the invocation inq -in C2F.inq. Here is what it looks like:

Create The Components
Components are created explicitly in script statements or implicitly by directives and groupings specified in the layout. We cover the latter when discussing the layout function in detail.
Components are created by declaring them just like any other variables. The following data types are available:
| Data Type | Component Type | Underlying Component |
|---|---|---|
| gWindow | A top-level window that interacts with the system window manager | JFrame |
| gDialog | A modal or non-modal dialog | JDialog |
| gIWindow | An top-level window suitable for use with a gDesktop | JInternalFrame |
| gBox | A horizontal or vertical box for use as an intermediate, grouping container | JPanel |
| gButton | A simple button | JButton |
| gToggle | A toggle button | JToggleButton |
| gCheck | A check box | JCheckBox |
| gRadio | A radio button intended for use with a gButtonGroup | JRadioButton |
| gTable | A table | JTable |
| gList | A list | JList |
| gTree | A tree | JTree |
| gToolBar | A tool bar container | JToolBar |
| gMenuBar | A menu bar for use as the menuBar property of a gWindow or gIWindow | JMenuBar |
| gMenu | A menu suitable for use in a gMenuBar or as a sub-menu in a gPopupMenu | JMenu |
| gLabel | A label | JLabel |
| gMenuButton | A simple menu button | JMenuItem |
| gMenuCheck | A menu check button | JCheckBoxMenuItem |
| gMenuRadio | A menu radio button | JRadioButtonMenuItem |
| gPopupMenu | A popup menu that can be placed on a component using the default or other popup event | JPopupMenu |
| gButtonGroup | Provides access to member radio buttons as a group | JPopupMenu |
| gTextField | A one-line text field | JTextField |
| gPasswdField | A one-line text field that hides its text | JPasswordField |
| gTextArea | A multi-line, single-style text area | JTextArea |
| gTextPane | A multi-line, multi-style text area | JTextPane |
| gTab | A tab container whose Inq children are the the root of the GUI under each tab | JTabbedPane |
| gSplit | A horizontal or vertical split pane | JSplitPane |
| gComboBox | A combo-box | JComboBox |
| gFileChooser | A file chooser | JFileChooser |
| gSpinner | A spin button capable of supporting various underlying data types | JSpinner |
| gDateChooser | A date chooser with popup calendar | com.toedter.calendar.JDateChooser |
| gProgressBar | A progress bar | JProgressBar |
| gSlider | A slider with a defined numeric range | JSlider |
| gDesktop | A desktop container to support gIWindow components | JDesktopPane |
The components our example uses are created by the following script fragment
// A top-level window. By setting its contextNode property to true we // are saying that events occurring at or below this point in the Inq // hierarchy will run with $this set to "win". gWindow win; win.properties.contextNode = true; // Create some GUI components gSlider slCelsius; gTextField tfCelsius; gLabel lCelcius; gSlider slFahr; gTextField tfFahr; gLabel lFahr;
Set The Required Property Values
Next, the script sets up any properties using each component's properties child:
slCelsius.properties.orientation = slFahr.properties.orientation = ORIENT_VERTICAL; slCelsius.properties.minimum = -273; slCelsius.properties.maximum = 100; slFahr.properties.minimum = call celciusToFahrenheit(celcius = -273); slFahr.properties.maximum = call celciusToFahrenheit(celcius = 100); slCelsius.properties.majorTickSpacing = 13; slFahr.properties.majorTickSpacing = 27; slCelsius.properties.paintTicks = slFahr.properties.paintTicks = true; slCelsius.properties.paintLabels = slFahr.properties.paintLabels = true;
Provided the underlying component supports it, properties can be both set and read in this way. All the JavaBeansTM single-value properties defined by the underlying component are available. Some components define additional properties as part of their implementation in the Inq run-time. These are detailed in the components reference section.
When Is The Context Established?
The window, win has its contextNode property set to true. Although, as stated above, this means that the context node for event handlers will be win, its important to note that this may not be the context prevailing when the createGUI() function is called. While the GUI is being created the context is often a higher-level part of the application, or even $root if we are creating the first window.
This temperature converter is an interactive script to simply mockup a GUI. As we will see when we discuss full client-server applications, it can be inconvenient to invoke any server-side services required during GUI setup when the context is not the one that GUI will run in. For this purpose, Inq defines a component event that is fired when that component's context becomes known. This happens during GUI layout, when the component is added to the Inq hierarchy such that an ancestor node has its contextNode property set to true. We will return to this subject when we cover client-server examples.
Attaching Views to Models
All components support Inq-defined properties that are used to specify the data they are rendering. Setting these properties establishes two things:
- tells the component where in the node space it must look to fetch the data it is displaying (and to update if the component can accept input);
- dispatches the events Inq raises on the data to the component, so that when the data is changed by client or server-side script the component will refresh.
Using the popular MVC paradigm, setting these properties is the way Inq binds a view to a model. Once established, the flows to implement MVC are handled by the Inq run-time. While the application may drive the GUI state from specific GUI event handlers, no scripting is required to implement MVC.
Complex components, such as tables, trees and the list forms implement more than one property for this purpose and use complex property values. Simple components rendering single values, like the ones used in this example, require only the renderInfo property to establish their MVC. Here is the script that does this for our temperature converter:
// Create two variables - the "model" data if you like. We use fixed-precision // numbers for convenience, as the maths cannot be accurate. Note that // sliders can only yield integers, but Inq can handle model data of any // non-integer type for them. decimal:2 win.vars.celcius; decimal:2 win.vars.fahrenheit; // Bind the two celcius views to the same model data.... slCelsius.properties.renderInfo = renderinfo($this.vars.celcius); tfCelsius.properties.renderInfo = renderinfo($this.vars.celcius, editable = true); // .... and the same for the fahrenheit views. These statements mean // that the GUI will update the model and changes to the model will update // the GUI. slFahr.properties.renderInfo = renderinfo($this.vars.fahrenheit); tfFahr.properties.renderInfo = renderinfo($this.vars.fahrenheit, editable = true);
The model data is two decimal values to hold the temperatures. These are declared underneath the window, win, because that is the context node.
The renderInfo Property
The renderInfo property accepts the result of a renderinfo() expression. In its simplest form, renderinfo has a single argument node reference, as in these examples. However, as well as specifying where in the node space the data is, renderinfo can also represent a number of other parameters that affect how this data will be rendered. The syntax of renderinfo is as follows:
"renderinfo" "("
( [<expression>]
| "typedef" "=" <field_reference>
| "format" "=" <expression>
| "label" "=" <expression>
| "width" "=" <expression>
| "editable" "=" <expression>
)
( ","
( "typedef" "=" <field_reference>
| "format" "=" <expression>
| "label" "=" <expression>
| "width" "=" <expression>
| "editable" "=" <expression>
)
)*
")"
The optional first argument is an expression whose result is the data to be rendered. If this, as in many cases, is a simple node reference then there are certain efficiency and functional benefits that we will cover shortly, however the expression can be anything.
The remaining arguments are also optional and independent of their order, so are named. To be valid, a renderinfo requires at least an expression or a typedef argument.
- typedef =
-
The typedef argument is a field reference to a typedef structure or a reference to a typedef alias. From foregoing typedef examples we could specify
typedef=Entity.GlobalLimit
and
typedef=FXRate
When used with an expression argument, the effect of typedef is to apply the format, width and label from the definition of the field or alias. If no expression is specified then Inq assumes a default node path of $this.<type_name>.<field_name> where <type_name> is the name of the typedef, or its override if it defines one.
The default path is suitable when resolving children of a node set structure, such as those Inq builds when applying non-unique keys. These structures are often displayed as a table or list form, so renderinfo expressions used for these components typically do not require the expression argument.
NoteAs a special case, the expression and typedef arguments can be combined to generate the default path. If the expression is (or in the current context evaluates to) a path(<node_reference>) then the specified node reference prefixes the default path implied by the typedef argument. We will return to this form of renderinfo when discussing the client-server examples. - format =
-
A string expression that specifies the format pattern. If present, the argument overrides any format implied by a typedef argument.
- label =
-
A string expression that specifies the label. If present, the argument overrides any label implied by a typedef argument.
- width =
-
An integer expression that specifies the width. If present, the argument overrides any format implied by a typedef argument. The width is used to dimension the component as a the character width of lower-case 'n' in the component's font. If the renderinfo applies to a table column then width dimensions the column. If neither typedef or width are present then a default width of 12 is assumed.
- editable =
-
If the component accepts input and has an editable and non-editable state then the editable boolean expression sets this state. By default, editable is false. A component's editable state (and that of complex component elements such as table cells) is also accessible through a property. If the application needs to change editable according to current state then it does so via property access and renderinfo only provides the initial value.
The temperature converter does not use typedef declarations at all and, because the data model uses fixed precision decimal values, need not use explicit formatting. As we will see when discussing this example's layout, the text fields are allowed to take a preferred size, so no width is specified either.
The renderinfo Expression
Model events dispatched to the component carry information about whether the node has been simply updated or wholly replaced in the node space. When renderinfo uses a single node path for its expression, either explicitly as in these examples or implicitly when specifying only a typedef argument, Inq uses the event detail to cache the resolved data node.
Once the node path has been resolved, there is no need to do so again when updates are dispatched. Inq only has to refresh the GUI component with the new value. If the event signals node replacement, however, Inq resolves the path again to refresh its cached value.
As stated above, A renderinfo's expression can be any Inq statement. Consider the following example of a label component that is set up to display audit information about when and by whom some data was last updated:
lLastUpdated.properties.renderInfo = renderinfo(
{
array lastUp = ("Last updated",
$this.vars.Trade.LastUpdated,
"By",
$this.vars.Trade.User);
render(lastUp, format="{0} {1,time,dd/MM/yyyy HH:mm:ss} {2} {3}");
});
In this case the expression is a block statement that declares an array used to format a parameterised string. Recall that the all Inq statements yield a value and that, for block statements, the value is the last statement executed. The render function accepts an expression and the same arguments as renderinfo to yield a formatted string.
Inq dispatches to this component when updates occur on $this.vars.Trade.LastUpdated or $this.vars.Trade.User. However, because a complex expression returns a temporary variable each time it executes, Inq cannot cache it and is forced to execute the expression for every dispatch.
In general, Inq will analyse complex renderinfo expressions to determine the node events that should be dispatched to a component. Only nodes at paths of $this can originate events so only these are considered. Any function calls in the expression will be walked and eligible node paths in the function body considered. This is a set-up overhead and not a run-time one, although it is assumed that evaluating the expression on every dispatch is acceptable for data that is predominately static. It is particularly appropriate for driving GUI state on the basis of user input, something that is shown in the example applications.
renderinfo and firemodel
When a GUI component updates its model data, the MVC design states that the component will fire a model event to notify other observers of the new model state. Inq components do not, by default, raise model events in this way because
- the component is often the only observer of the data;
- there can be several options for component events that need to raise model events, for example a text field may update its model when the enter key is pressed, when keyboard focus is lost or on every key stroke.
As we will see when discussing component event handlers below, Inq script can configure which component events will fire model events with the firemodel argument. Note, though, that Inq can only fire model events when the renderinfo expression is a simple node path. If a complex expression is used then firemodel is ignored.
Performing The Layout
Achieving a satisfactory layout of components that also exhibits the desired behaviour when the user resizes the GUI can be a very time-consuming exercise and not especially edifying. The approach taken by Inq owes much in its inspiration to the XMT Toolkit originally written by David Flanagan. It avoids the need for the programmer to master the many complex layout managers available and expresses the layout in a way that is easy to visualise and maintain.
The layout function performs a component layout given the minimum of
- the set of components;
- the parent into which the layout will be made;
- the layout string specification.
A full discussion of layout is covered in its own section. For now we will discuss those features used by our example, whose layout invocation is
layout(., win, "Row
{
Margin d:3 Etched Lowered Caption tl $catalog.{$root.i18n}.celcius; Column
{
Geometry d:f lCelcius
Nofocus Geometry xy:fv slCelsius
Geometry xy:vf tfCelsius
}
Margin d:3 Etched Lowered Caption tl $catalog.{$root.i18n}.fahrenheit; Column
{
Geometry d:f lFahr
Nofocus Geometry xy:fv slFahr
Geometry xy:vf tfFahr
}
}");
Layout Arguments
The first argument to layout is the component set. The components we are laying out are all declared on the stack, which is yielded by the node path ".". The layout specification itself refers to the components by their name within the set. This name is a single identifier, not a node path, so all the components must reside in the same Inq map container and therefore have unique names.
The second argument is the Inq node into which Inq the components will be placed. As components are referenced in the layout specification, so they are added to this node. Remember that Inq maps operate a unique key set, so the layout will fail if there is already a child in the specified Inq node with the same name as any of the components. It is this aspect of layout processing that creates the Inq hierarchy introduced earlier and if the node, or an ancestor of it, is a context node, this node will become the context for each component as it is processed by layout. In our example the Inq root and the GUI root are the window.
The third argument is the layout specification. This is a string that is parsed to create the GUI hierarchy. The major layout concept (though there are others supported) is that of nested rows and columns. A row or column is a container made by layout as the specification is parsed to group its contents, contained within matching braces.
The Layout Specification
Rows and columns are introduced with the keywords Row and Column. Generally, keywords in the layout syntax always begin with an upper case letter. Qualifiers apply to the named component that follow. Layout artifacts, such as rows and columns are unnamed components that can also accept qualifiers. If followed by an identifer, an unnamed component becomes an Inq node in the same way as if it had been declared explicitly prior to layout.
When considering the desired layout for the temperature converter we settle on a row containing two children, each of which is a column containing the label (for the image), the slider and the text field. As each of the row children represents a temperature scale it is given the following qualifiers:
- a margin of 3 pixels dimension;
- an etched, lowered relief border;
- a caption at the top and through the border, justified to the left.
The caption is an expression followed by a semi-colon, that must evaluate to a string. In this example the expression is a path that references the system catalog with a parameterised path intended to take account of an internationalisation setting.
The Geometry qualifier states how the component it applies to will resize. When absent, as for the columns, the component is fully resizable in both the X and Y directions. For the components within the columns, the geometry constraints are set as follows:
- The labels are fixed in both the X and Y directions. This means that, as the window is resized, the labels will remain at their initial dimensions. There would be no consequence visually if the labels were allowed to resize in the X direction and, as they do not have any event handlers attached to them, there are no functional implications either. Fixing them in the Y direction is important because we want any resizing on this axis to affect the sliders only. To fix the label dimensions the geometry setting d:f is applied. The dimension letter d is short for both x and y. The value f means "fixed".
- The sliders have the geometry specifier xy:fv, meaning they can grow and shrink on the Y axis but are fixed along X. As for the labels, the same visual effect would be achieved if the sliders were fully floating (if no Geometry were specified) however constraining their width means that their mouse input area is sensible. Try the example changing the geometry value to xy:vv and (subject to any look and feel dependencies) you will see that the sliders are clickable all the way to the window extremities.
- Finally, for the text fields we would like them to remain a fixed height but be allowed to grow along the X axis, in other words the opposite behaviour to the sliders.
The Nofocus qualifier applied to the sliders means they will not accept the keyboard focus, leaving the only focusable components in this example as the text fields.
The Resulting Node Hierarchies
Returning to the subject of instance hierarchies, after layout is complete the component and Inq node trees are as shown in the figures below. Click on the images for a larger view.
![]() |
![]() |
| Inq Hierarchy | GUI Hierarchy |
The GUI hierarchy includes the necessary artifacts to achieve the desired layout. The Inq hierarchy places the components of interest under the context node, which is the window. The active components are viewing the model data rooted at $this.vars, though which all further control of the application takes place.
Initialising The Model
The next two lines of the example initialise the model data.
// Just some initialisation win.vars.celcius = 0; win.vars.fahrenheit = call celciusToFahrenheit(celcius = win.vars.celcius);
In this case the model data is defined in the client itself. In client-server applications its common to make a service request to retrieve data and set up the client's server-side state. These service requests are frequently made from a handler of the context established event, set up on any suitable component. In any case, the purpose of any model data initialisation is to fire model events that, in turn, will initialise the components to which those events are dispatched. Event dispatching by the model and data resolution by the GUI components through the expression of their renderinfo property is only enabled when the context is established and so must take place after this time.
The temperature converter has two views each of $this.vars.celcius and $this.vars.fahrenheit, the slider and the text field. Binding the view to the model has the effect of making the container vars emit events when script mutates fields within it. This is an entirely free-form way of creating active model data in the client and can be used for any client-side-only state required to drive the GUI. In our example, initialising the model as above sets up the GUI so that it shows zero celcius and 32 fahrenheit.
Input Validation
The following lines establish the validateInsert property, which must be a func whose expression is a call statement:
// Setup the "validateInsert" property on the text fields. // $catalog.guiFuncs.numericFloat is a predefined function that allows // only numeric characters to be entered. // See classpath://inq/gui/verifiers.inq tfCelsius.properties.validateInsert = tfFahr.properties.validateInsert = $catalog.guiFuncs.numericFloat;
The validateInsert function is called whenever text is typed or pasted into the text field or when its text property changes. This includes model events that cause the text field's content to change. The function returns null to veto the text or the string value to be inserted otherwise.
A detailed discussion of validateInsert and the companion inputVerifier property functions is covered in the detailed sections (to be written).
Establishing Component Event Handlers
An application responds to user input, or other component events, by establishing event handlers with the Inq gEvent function. Here is the syntax of gEvent
"gEvent" "(" <expression> ","
( [<call_statement>] ","
[ "event" "=" "(" <event_list> ")" ] ","
[ "gModify" "=" "(" <modifier_list> ")"] ","
[ "gDialog" "=" ( "gDialogok" | "gDialogcancel" ) ] ","
[ "consume" "=" "(" <boolean_literal> ")" ] ","
[ "firemodel" "=" "(" <boolean_literal> ")" ]
)
")"
The expression argument must reference a component node. To be valid, in addition gEvent requires at least a call statement or specify firemodel=true. Here are some uses of gEvent in the temperature converter:
gEvent(slCelsius, call celciusToFahrenheitCB(), firemodel=true); gEvent(tfCelsius, call celciusToFahrenheitCB(), firemodel=true); gEvent(slFahr, call fahrenheitToCelciusCB(), firemodel=true); gEvent(tfFahr, call fahrenheitToCelciusCB(), firemodel=true); // Just the exit event. By default window-close does nothing. gEvent(win, call exitCB(), event=(gWclosing));
The Handler Function
If a component event requires specific processing (that is it will do more than just fire a model event) then this is scripted as a function and gEvent uses the call statement argument. The temperature converter's call-backs just call helper functions to perform the conversion. These functions are entirely stack-based and do not assume any particular structure prevailing below $this. This means that functionality is separated from model data and can be used more generally.
Passing Parameters To The Event Handler
The call statement can pass parameters to the call-back function, so an alternative way to write the gEvent statements is
gEvent(slCelsius, call celciusToFahrenheitCB2(temperatures = $this.vars), firemodel=true); gEvent(tfCelsius, call celciusToFahrenheitCB2(temperatures = $this.vars), firemodel=true); gEvent(slFahr, call fahrenheitToCelciusCB2(temperatures = $this.vars), firemodel=true); gEvent(tfFahr, call fahrenheitToCelciusCB2(temperatures = $this.vars), firemodel=true);
This is another pattern for reducing the dependency of call-back functions on the context. Notice that the argument passed is $this.vars and not individual fields, to avoid field ripping.
Predefined Arguments To Event Handlers
If we uncomment the line writeln($catalog.system.out, $stack); in celciusToFahrenheitCB then we can look at the predefined arguments Inq places on the stack. Here is the output we get for a call-back from the celsius slider:
{@eventId=E_CHANGE, @component={renderedValue=$this.vars.celcius}}
Inq passes the event ID, in this case E_CHANGE, and the originating component at the path @component. When single-value components are rendering a simple node path, Inq includes the indirection renderedValue. This allows the event handler to access the component's model data using the path @component.renderedValue without having to know the specific path that was used in its renderinfo.
The more complex components place additional information in the event. These are covered in the reference section and the example applications.
Specifying The Events
The Default Event
The general syntax for a gEvent expression specifies the events to be processed in the event = (<event_list>) argument. In the examples this argument is omitted, meaning that Inq will use the default event for the component. Only those components that have an obvious default support call-back definition in this way. These are currently defined as:
| Component | Default Event | Comments |
|---|---|---|
| gTextField and gPasswdField | gAction | |
| Plain, toggle or radio buttons and their menu variants | gAction | |
| gComboBox | gAction | |
| gTab | gChange | |
| gDateChooser | gChange | |
| gSlider | gChange | |
| gMenu | gMenuSelected | |
| gButtonGroup | gAction | Events are solicited from the group, not the group's radio items |
Specific Event Types
Specific events are named, for example these lines solicit the focus lost event from the text fields:
gEvent(tfCelsius, call celciusToFahrenheitCB(), event=(gFocuslost), firemodel=true); gEvent(tfFahr, call fahrenheitToCelciusCB(), event=(gFocuslost), firemodel=true);
Binding Properties
Attaching a view to its model is a special case of Inq's ability to bind any property a component supports to a data item. The gProperty function accepts a component, a property name and a renderinfo expression to achieve this.
"gProperty" "("
<expression>
<identifier>
<renderinfo>
")"
The expression must resolve to a GUI component. The identifier is a literal property name the component must support. In the temperature converter we have bound the foreground property of the text fields to the celsius temperature as an illustration:
gProperty(tfFahr,
foreground,
renderinfo({ $this.vars.celcius < 0 ? $this.vars.blue
: $this.vars.red; }));
gProperty(tfCelsius,
foreground,
renderinfo({ $this.vars.celcius < 0 ? $this.vars.blue
: $this.vars.red; }));
The text colour is blue below zero celsius and red otherwise. Property bindings can be set up to drive GUI state from the data in the node space, for example sensitising components according to data state. Further cases are shown in the example applications.


