Platinum Partner
java

Pivot: A Practical Example, Part 1 - UI Markup Using WTKX

This is the first in a series of five articles that walk through the implementation of a simple but practical Pivot application called Stock Tracker. This section focuses on building a user interface using WTKX, Pivot's XML markup language. The next article will cover event handling in Pivot. --Greg Brown

The user interface of a Pivot application is often defined using XML markup rather than Java code. This is because XML can be considerably easier to read and work with than straight Java. The hierarchical structure of an XML document more closely parallels the structure of the component hierarchy, which makes it easier to visualize the resulting output.

Pivot allows developers to split the definition of the user interface into multiple WTKX files. This allows each piece to vary independently and makes the overall structure easier to manage. The layout of the Stock Tracker application is divided into two WTKX documents: one that sets up the main structure of the application and another that defines the layout for the quote detail:

We'll take a look at the top-level markup in stocktracker.wtkx first, concentrating on the two main page elements, TablePane and TableView. Then we'll investigate the markup for the stock quote detail, which uses the Form container and lays the ground work for the data binding support we'll discuss in a later section.

TablePane

The root node of stocktracker.wtkx is a TablePane element. A TablePane is a type of layout container that arranges its children in a manner similar to an HTML table.

Like HTML tables, TablePane contents are organized into rows and columns. Unlike HTML, however, which may attempt to infer a table structure from its contents, a TablePane's structure must be defined in advance, as shown below:

<TablePane styles="{padding:8, spacing:6}">
<rows>
<Row height="-1"/>
<Row height="1*"/>
<Row height="-1"/>
<Row height="-1"/>
</rows>
<columns>
<Column width="244"/>
<Column width="1*"/>
</columns>
...
</TablePane>

TablePane row heights and column widths can be specified as either automatic, absolute, or relative:

  • A value of -1 specifies an automatic size; the table pane will determine the height of the row as the maximum preferred height of all components in that row; the same applies to automatic column widths.

  • An explicit, non-negative value defines an absolute width or height, specified in pixels.

  • A number followed by an asterisk specifies a relative size; the width or height is determined by applying a weighted percentage of the space left over after subtracting the width or height of all automatic and absolute columns and rows (which may be 0).

In the example above, the second row is given a relative height that will assign it all of the available space once the other rows are accounted for; all other rows are given an automatic height. The first column is given an explicit width of 244 pixels, and the second column is given 100% of the remaining width.

The following graphic shows the resulting layout (the blue lines represent the table cell boundaries):


Stock Tracker table pane cells

Styles

Note the styles attribute of the TabPane element. Styles are used to customize the appearance of a component, and are specified in WTKX as a JSON-formatted collection of name/value pairs. All WTKX elements support the styles attribute, but each element may support a different set of styles depending on the currently installed skin.

In this example, the TablePane is assigned a padding value of 8 and a spacing value of 6. Padding represents the amount of space that the table pane will leave around its components, and spacing is the size of the gap the table pane will leave between components.

Table Pane Cells

The actual content of a table pane is specified in the <cells> element. Each immediate child element of the <cells> element is considered a child component of the table pane. The row and column in which the child will be placed is specified by the row and column attributes attached to the sub-element, as shown below:

<Label row="0" column="0" text="%stockTracker"
styles="{font:'Verdana bold 14', verticalAlignment:'center'}"/>

Note the use of the percent symbol in the text attribute value. This tells the WTKX component loader that value of the text attribute should be provided by a resource string with the name specified in the attribute, minus the percent sign. This will be discussed in more detail in the Localization section.

TableView

The TableView component is used to present tabular data in a grid of rows and columns. While it bears a superficial resemblance to TablePane, the purpose of the two components is quite different: TablePane is a container; its primary purpose is to lay out sub-components, and its structure is most often defined at design time. TableView is a component that acts as a view on a set of model data and is most often populated at run time.

Like TablePane, TableView's columns must be defined up front. However, since the contents of a table view are generally not known until run time, it is not necessary to define a table view's row structure - this is provided by the table view's model data when the table view is populated.

TableView is used by the Stock Tracker demo to display a selectable list of stock quote data. The following listing shows the markup used by the demo to create the table view:

<TableView id="stocksTableView" selectMode="multi"
styles="{showHorizontalGridLines:false}">
<columns>
<Column name="symbol" headerData="'%symbol'" width="80"/>
<Column name="value" headerData="'%value'" width="80"
cellRenderer="pivot.wtk.content.TableViewNumberCellRenderer"
cellRendererStyles="{horizontalAlignment:'right'}"
cellRendererProperties="{numberFormat:'$0.00'}"/>

<Column name="change" headerData="'%change'" width="80"
cellRenderer="pivot.tutorials.stocktracker.ChangeCellRenderer"
cellRendererStyles="{horizontalAlignment:'right'}"
cellRendererProperties="{numberFormat:'+0.00;-0.00'}"/>
</columns>
</TableView>

Note that the TableView is given an id attribute value of "stocksTableView". This is used to associate the table view with an instance of TableViewHeader, which is discussed in the next section. ID values are also used to gain access to WTKX components from Java code when wiring up event handlers. This will be discussed in more detail in the forthcoming section on event handling.

The selectMode="multi" attribute specifies that a user may select multiple rows in this table view. Other possible values for selectMode are "single" and "none". Setting the showHorizontalGridLines style to false tells the table view not to draw horizontal grid lines separating its cells. Vertical grid lines will still be drawn; however, these can be turned off as well by setting showVerticalGridLines to false.

TableView Columns

The application defines three columns for the table view: one for the stock ticker symbol, another for the stock's current value, and a third for the change percentage. The name attribute defines the name of the column. It is not visible to the user, but is used as a key to map values in each row to their appropriate place in the table view; the value of the headerData attribute is what is actually shown to the user in the table view header. Note that the header data for each column is specified as a resource name so the header text can be localized.

The width of each column in this example is set to an absolute value of 80 pixels. Table view columns can also be assigned a relative width, but they do not support the automatic widths supported by the TablePane component.

The "value" and "change" columns define several additional attributes related to cell renderers. By default, table cells are drawn using a renderer that presents the cell's contents as a text string. Custom cell renderers can be used to present cell data formatted as a number, date, or otherwise. The Stock Tracker demo uses the built-in number cell renderer to format the value column and a custom renderer to display the values in the change column using either red or green text depending on the change direction. A complete discussion of cell renderers is outside the scope of this tutorial, but the source code for the custom renderer can be viewed here.

TableViewHeader

Pivot's TableView component does not include built-in header support; rather, support for column headings and resizing is provided by the TableViewHeader component. This is because TableView (and other similar components including ListView and TreeView) do not include built-in support for scrolling. Scrolling is provided by the ScrollPane class, which is discussed in the next section.


Stock Tracker table view and table view header

The following markup is used to declare the table view header component for the Stock Tracker demo:

<TableViewHeader tableViewID="stocksTableView"/>

That's it! The header automatically associates itself with the table view identified by the tableViewID attribute - no additional markup or coding is necessary.

Though it is not shown in the Stock Tracker demo, TableViewHeader can also be used to provide support for column sorting. An example of this is shown in the "kitchen sink" demo application.

ScrollPane and Border

Pivot components do not manage scrolling internally - all scrolling in Pivot is handled by the ScrollPane class (more specifically, by an instance of the Viewport class, but the distinction is not relevant to this discussion). A scroll pane provides a windowed view onto another component and allows a user to select which part of the component is shown by dragging a horizontal or vertical scroll bar.

The component being scrolled is called the "view", and is specified as a child of the ScrollPane's <view> element. ScrollPane also provides support for fixed row and column headers that scroll with the view but always remain visible. These are defined using ScrollPane's <rowHeader> and <columnHeader> sub-elements. A TableViewHeader is often used as a column header for a TableView instance, but a row or column header can be an instance of any type of component - for example, an application developer might create a "ruler" component to use as a header in a drawing program to help a designer visualize the scale of a large scrollable image.

Stock Quote Detail

The stocktracker.wtkx file defines the overall structure of the application, and the layout of the quote detail content is defined separately in stocktracker.detail.wtkx. Yet, when the application is run, it appears as though the entire application was defined in a single WTKX file. How does this work? The key is the Component tag:

<Component namespace="detail" src="stocktracker.detail.wtkx"/>

The Component tag acts as a sort of "include" statement - it tells the component loader that a component should be placed here, but that the component's content is not defined in this file. The location of the actual content is specified by the src tag and is either an absolute (beginning with a slash character) or relative (no slash) path to another WTKX file. Relative paths are resolved in the context of the current WTKX file, and absolute paths are resolved in the context of the application's classpath. Fully-qualified URLs (those that begin with a protocol) are not supported.

In addition to the src attribute, the Component tag requires a value for the namespace attribute. This is used to partition the ID values defined in each WTKX file into separate namespaces, allowing developers to avoid naming collisions. Components are referenced by concatenating the namespace(s) with the component's ID; for example, the path "detail.changeLabel" refers to the Label with ID "changeLabel" defined in the stocktracker.detail.wtkx file.

Forms

Though the top-level element of the detail page is a FlowPane, most of the layout is handled by the Form container. Forms arrange their child components in a vertical stack, similar to a vertical FlowPane, prepending a text label to each field.


Stock Tracker detail view using Form container

A field's label is defined by a label attribute attached to the child element, as shown below:

<Form styles="{rightAlignLabels:true, fieldAlignment:'right'}">
<Label label="%value" textKey="value"/>
<Label id="changeLabel" label="%change" textKey="change"/>
<Label label="%openingValue" textKey="openingValue"/>
<Label label="%highValue" textKey="highValue"/>
<Label label="%lowValue" textKey="lowValue"/>
<Label label="%volume" textKey="volume"/>
</Form>

Since the quote data is read-only, the "fields" in this example are actually themselves Label instances. Their form label values are specified as string resources so they can be easily translated.

Data Binding

Each Label in the form defines a textKey attribute. This attribute specifies the name of the property from which the label will obtain its text value during data binding. Specifically, it is the name of the value's key in the "bind context", an instance of Dictionary<String, Object> that is passed to the load() method of the Form instance, and then recursively to the Labels themselves. This will be discussed in more detail in the data binding section.

Though data binding is often used to populate a Form's contents, it can be used with any container class. For example, text input components in a FlowPane can also be data-bound, as can a list view in a card pane; there is nothing special about the Form class that supports data binding - it is supported by all container types and many component classes.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}