Skip to content

Declarative UI for Java

License

Notifications You must be signed in to change notification settings

HTTP-RPC/Sierra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Releases Maven Central javadoc

Introduction

Sierra is an open-source framework for simplifying development of Java Swing applications. It is extremely lightweight and has minimal external dependencies. The project's name comes from the nautical S or Sierra flag, representing the first letter in "Swing":

This guide introduces the Sierra framework and provides an overview of its key features.

Getting Sierra

Sierra is distributed via Maven Central at org.httprpc:sierra. Java 17 or later is required.

Sierra Classes

Sierra provides the UILoader class, which can be used in conjunction with the following types to to declaratively establish a hierarchy of user interface elements:

  • RowPanel, a container that automatically arranges sub-components along the x-axis
  • ColumnPanel, a container that automatically arranges sub-components along the y-axis
  • StackPanel, a container that automatically arranges sub-components by z-order
  • Spacer, a component that provides fixed or flexible space between other components

These types offer an alternative to the standard Java layout managers, which can often be limiting or difficult to use in practice. RowPanel optionally aligns sub-components to baseline, similar to FlowLayout. ColumnPanel optionally aligns sub-components to a grid, similar to an HTML table or GridBagLayout.

Sierra also includes the TextPane and ImagePane components, which provide an alternative to JLabel for displaying basic text or image content, respectively. TextPane supports wrapping text without requiring HTML, and ImagePane supports scaling without requiring an intermediate BufferedImage.

For example, the following markup declares a column panel containing a graphic and a simple greeting:

<column-panel padding="8" opaque="true" background="white">
    <image-pane image="world.png" scaleMode="fill-width"/>
    <text-pane text="Hello, World!" horizontalAlignment="center"/>
</column-panel>

This markup could be deserialized and set as the content pane of a frame or dialog as follows:

setContentPane(UILoader.load(this, "greeting-test.xml"));

The resulting output is shown below:

The complete source code for this example can be found here.

Elements

XML elements represent component instances. Most Swing and all Sierra components are supported by default. Support for additional elements can be added via the bind() method of the UILoader class.

Elements can be nested to create a component hierarchy. For example:

<column-panel spacing="8" padding="8">
    <column-panel>
        <check-box text="checkBox1"/>
        <check-box text="checkBox2"/>
    </column-panel>

    <row-panel spacing="8">
        <button name="button" text="executeTask"/>
        <label name="label" foreground="gray"/>
        <spacer weight="1"/>
        <activity-indicator name="activityIndicator" indicatorSize="18"/>
    </row-panel>
</column-panel>

Attributes

XML attributes generally represent component properties. For example, this markup creates an instance of TextPane and sets its "text" property to "Hello, World!":

<text-pane text="Hello, World!" horizontalAlignment="center"/>

Numeric and boolean values are automatically converted to the appropriate type. Properties that expect values defined by the SwingConstants class (such as "horizontalAlignment") can be specified via a lowercase version of the constant name. Enum values are specified using kebab case.

Color and Font Values

Color and font properties can be specified using the formats supported by Color#decode() and Font#decode(), respectively. For example, this markup creates an instance of JLabel and sets its "foreground" property to gray:

<label name="label" foreground="#808080"/>

Colors and fonts can also be referenced by name:

<label name="label" foreground="gray"/>

Sierra includes support for the 16 "basic" web colors by default. Support for additional colors and fonts can be added via the define() methods of the UILoader class.

Image and Icon Values

Image and icon properties can be specified via a path to an image document on the application's classpath. The path is relative to the document's "owner", the value passed as the first argument to UILoader#load(). For example:

<image-pane image="world.png" scaleMode="fill-width"/>

Icon support is currently limited to SVG documents and requires the FlatLaf Extras library:

<toggle-button name="alignLeftButton" icon="format_align_left_black_18dp.svg" FlatLaf.style="buttonType: toolBarButton"/>

Border and Padding Values

The "border" and "padding" attributes can be used to specify a component's border and padding, respectively. These attributes mirror the corresponding concepts in the CSS box model. For example, this markup creates an instance of JLabel with a light gray line border and four pixels of padding on each side:

<label text="pageStart" horizontalAlignment="center" border="silver" padding="4"/>

Border thickness can be specified as shown below:

border="#00ff00, 4"

Padding values for all four sides can be specified in top, left, bottom, right order:

padding="8, 8, 8, 8"

Weight and Size Values

The "weight" attribute specifies the amount of excess space in a container that should be allocated to a component, relative to other weighted components in the container. When applied to a Spacer instance, it creates a "glue" component that automatically shrinks or stretches depending on the size of its container. However, weights are not limited to spacers and can be applied to any component type:

<row-panel spacing="4" weight="1">
    <label text="lineStart" font="h2" horizontalAlignment="center" border="silver" padding="4"/>
    <label text="center" font="h1" horizontalAlignment="center" border="silver" padding="4" weight="1"/>
    <label text="lineEnd" font="h2" horizontalAlignment="center" border="silver" padding="4"/>
</row-panel>

The "size" attribute specifies a fixed dimension for a component. It is typically used with Spacer instances to create "struts" between components, as an alternative to the "spacing" property provided by RowPanel and ColumnPanel:

<column-panel spacing="4" padding="8">
    <row-panel>
        <button text="1a"/>
        <spacer size="4"/>
        <button text="1b"/>
        <spacer size="4"/>
        <button text="1c"/>
        <spacer weight="1"/>
    </row-panel>
    
    ...
    
</column-panel>

FlatLaf Styles and Style Classes

FlatLaf style and style class values can be specified via the "FlatLaf.style" and "FlatLaf.styleClass" attributes, respectively. For example, this markup applies the "h2" style class to a JLabel instance used by a list cell renderer:

<row-panel spacing="4" padding="4" opaque="true">
    <image-pane name="imagePane" size="30" scaleMode="fill-width"/>

    <column-panel>
        <label name="nameLabel" FlatLaf.styleClass="h4"/>
        <label name="descriptionLabel"/>
    </column-panel>
</row-panel>

See CellRendererTest.java for more information.

Element Names

The "name" attribute associates an identifier with a component. The value is automatically injected into a field with the same name defined by the document's owner (called an "outlet").

For example, the following markup declares outlets named "greetingButton" and "greetingLabel":

<button name="greetingButton" text="prompt"/>
<label name="greetingLabel" horizontalAlignment="center"/>

When the load() method returns, the corresponding fields in the owner will be populated with the instances declared in the markup:

public class ActionTest extends JFrame implements Runnable {
    private JButton greetingButton;
    private JLabel greetingLabel;
    
    ...
}

See ActionTest.java for more information.

Resource Bundles

If a non-null value is passed as the third argument to the load() method, values of text properties are considered resource keys and are used to look up the associated strings in the provided resource bundle. For example:

<column-panel spacing="4" alignToGrid="true" padding="8">
    <row-panel alignToBaseline="true">
        <label text="firstName" alignmentX="1.0"/>
        <text-field columns="12" alignmentX="0.0"/>
    </row-panel>

    <row-panel alignToBaseline="true">
        <label text="lastName" alignmentX="1.0"/>
        <text-field columns="12" alignmentX="0.0"/>
    </row-panel>

    <row-panel alignToBaseline="true">
        <label text="streetAddress" alignmentX="1.0"/>
        <text-field columns="24" alignmentX="0.0"/>
    </row-panel>
    
    ...
    
</column-panel>
title = Form Test

firstName = First Name
lastName = Last Name
streetAddress = Street Address

...

Cell Alignment

When grid alignment is enabled in a ColumnPanel, the sub-components (or "cells") of every RowPanel in the column are vertically aligned in a grid, as in a spreadsheet or HTML table. The width of each sub-column is determined as the maximum preferred width of the cells in that column (i.e. the components having the same index in each row).

Cell contents are aligned based on the component's x and y alignment values (returned by getAlignmentX() and getAlignmentY(), respectively). For most components, the default is 0.5, indicating that the component should fill the entire cell along both axes. Values between 0.0 and 0.5 will align the component to the cell's leading or top edge, and values between 0.5 and 1.0 will align the component to the cell's trailing or bottom edge. In both cases, a proportional amount of the excess space will be allocated to the component. A value of 0 or 1 will result in no excess space being given to the component (i.e. it will be aligned to the appropriate edge and will be given its preferred size along that axis).

For example:

Utility Components

In addition to the features outlined above, Sierra also includes some common user interface elements not provided by Swing.

Menu Buttons

The MenuButton component displays a popup menu when pressed. For example:

See MenuButtonTest.java for more information.

Date and Time Pickers

The DatePicker and TimePicker components allow a user to select a local date and time, respectively:

These classes are localized. See DateTimePickerTest.java for more information.

Suggestion Pickers

The SuggestionPicker component allows a user to choose from a list of predefined values:

See SuggestionPickerTest.java for more information.

Activity Indicators

The ActivityIndicator component shows indeterminate progress:

See ActivityIndicatorTest.java for more information.

Focus Management

The ScrollingKeyboardFocusManager class ensures that components are automatically scrolled into view when focused (something that Swing oddly does not do by default). It can be installed at application startup as follows:

KeyboardFocusManager.setCurrentKeyboardFocusManager(new ScrollingKeyboardFocusManager());

See FormTest.java for more information.

Task Execution

The TaskExecutor class performs a task in the background and and invokes a callback on the UI thread when the task is complete:

public <T> void execute(Callable<T> task, BiConsumer<T, Exception> handler) { ... }

For example:

taskExecutor.execute(() -> {    
    // Perform long-running task that may throw
    return result;
}, (result, exception) -> {
    if (exception == null) {
        // Handle success
    } else {
        // Handle failure
    }
});

Internally, tasks are submitted to an executor service provided to the TaskExecutor constructor. See TaskExecutorTest.java for more information.

Complete Example

The following is a complete example of an application built using Sierra. It uses the Tiingo End-of-Day API to retrieve historical stock pricing information:

The application consists of the following source files:

An API token is required and must be specified as a system property at application startup:

-Dtoken=<Tiingo API Token>