: Coding

This page introduces the basics of coding a GUI surface based on the core Superficial concepts of facets, targets and retargeting: On-page applets demonstrate how you can use the Facets implementation of Superficial to

This page and its applets are at the beta stage of development. Please notify bugs here.

Creating an application

Below is an applet running the Superficial implementation of "Hello World".


You can view its complete code in a window or in the code viewer (itself a demo application).

HelloLabel is certainly a novel, and rather elaborate, way to display what is really just a Swing label. Why split the code between two methods, and how do they relate to each other? What are these STarget, STextual, SFacet, STargeter types? What is a FacetsFactory, and what does a STextual.Coupler do?

These questions are answered in some detail below: the underlying point is that Superficial is probably the first truly high-level framework for GUI development. In Superficial you don't define a GUI and then bind data to it - you define the data first, then (after a bit of magic performed for you by the framework) the GUI that gives access to it.

In this respect Superficial conforms to the Model-View-Controller (MVC) paradigm: it defines a GUI surface in terms first of its targets, and only then of the facets that expose them to user view and control; targets and facets are linked by retargeting which avoids the need for explicit data binding.

The target

The two methods of HelloLabel (both called ultimately from the init method of Applet) construct the simplest possible GUI application surface by defining a single text target, which in turn has a single exposing label facet.

The first method called is newBasicTargets which returns an array of STargets, the supertype of all target implementations. In HelloLabel the array comprises a single STextual which represents a simple text string, of which the initial (and in HelloLabel unchanging) state is passed to the constructor.

Of the other two parameters to the STextual, title is returned by all STargets for use as a caption by exposing facets. Since this STextual is to be exposed by an uncaptioned label facet, a string is passed that documents this.

The Coupler parameter is a minimal example of an important feature of the Facets implementation of Superficial as described later.

The facet

Since newBasicTargets returned a STextual to be exposed in the GUI of HelloLabel, it makes sense for newBasicPanelFacets to return a SFacet that will create and manage a suitable Swing label. The puzzle is that of the two types passed to newBasicPanelFacets, neither bears any obvious relationship either to the STextual or to Swing.

The Swing issue is most easily resolved. Because a Superficial GUI is defined purely in terms of facets, the Facets implementation can hide details of the GUI toolkit from client code using an abstract FacetsFactory. This has a wide range of methods supplying instances of SFacet that in turn create and manage GUI widgets.

For HelloLabel the only facet required is one that creates a simple label, so the method to call is textualLabel. Like most methods of FacetsFactory this takes two parameters: a STargeter and one or more concatenated HINT constants that can supply low-level facet policy. But where does the STargeter come from?

The targeter

The STargeter array passed to newBasicPanelFacets has a single member that can be used to create a label facet exposing the STextual created in newBasicTargets. So STargeters must be related in some way to STargets - but how?

During application construction each STarget creates a corresponding STargeter, which has the following characteristics:

  • It is retargeted (passed a reference) initially on the STarget that created it.
  • It can have any number of SFacets attached to it.
  • It can retarget these SFacets on its current STarget target.

It is this relationship between targeters, targets and facets that makes the Superficial retargeting mechanism so powerful.

The initial retargeting takes place before the STargeter is passed to newBasicPanelFacets, where the FacetsFactory is used to create and attach a SFacet to it. Once the Swing GUI has been created as described below, the STargeter will in turn retarget the SFacet, and this will set its JLabel widget with the text of the STextual.

The GUI

A major advantage of the Superficial approach is that it can shield client code completely from the details of low-level GUI construction. (Though in practice the Facets implementation allows surprisingly fine control of the detailed appearance of a GUI, and even the use of custom Swing components.)

All you need to know when coding with Superficial is that having defined your STargets and attached SFacets to the STargeters that they create, you can rely on the framework to do the rest.

If you're curious to see what actually happens, you can start at the top with demo.DemoSurface which manages the application creation and retargeting sequences for CodingApplets. However even these are performed at a high level of abstraction - if you insist on finding Swing code, you'll need to peer behind another layer or so of interfaces.

But why worry about these details? Much better move on to the next applet, and find out how Superficial helps you handle input to your application.

Input and updates

HelloLabel demonstrates the basics of a constructing a Superficial surface, but its simple label facet can't show how Superficial handles input to a GUI.

HelloField is a slightly more elaborate version of HelloLabel that exposes the greeting text not just with a label, but also with a text field that allows the the greeting to be edited. This enables it to demonstrate

  • how Superficial handles updating of targets and facets transparently to client code
  • how a single target can be shared by multiple facets
  • how facets hide code complexity and promote code re-use


Given the substantial increase in functionality provided by HelloField, it may surprise you how little the code differs from HelloLabel.

  • The STextual is constructed with a title that can be used by an edit field facet, and a Coupler that defines policy for the facet.
  • Such a facet is returned by newBasicPanelFacets together with the label facet.
  • And that's it! How does that work?

The edit field facet

The edit field facet returned by textualField in the FacetsFactory demonstrates how Superficial's high-level approach shields client code from low-level GUI complexities, while at the same time promoting code re-use.

To start with, the facet creates and manages not just a single widget, but a JPanel containing both a JLabel and a JTextField. The JLabel is set to the title property of the STextual; the JTextField allows editing of the STextual's current state.

Now try editing in HelloField and you will find the JTextField has more intelligence than you might expect. It won't allow you to blank it (this would make a nonsense of most texts); pressing Esc reverts its state while Enter commits the edited state.

Because the code that constructs the facet's widgets and enables it to handle input intelligently is hidden behind the SFacet reference, client code is hugely simplified. Code re-use is guaranteed because its functionality is available from every instance of FacetsFactory.

Moreover, the edit field facet appears magically bound to the label facet. The label reflects even uncommitted edits, while reverting the field reverts the label as well. Yet there's no code in HelloField for this binding (the Coupler only sets a flag as described below) - how is it defined?

Binding targets and facets

Compared with HelloLabel, HelloField can demonstrate much more clearly the advantages of Superficial for data binding.

Sharing a target

An obvious advantage is the ease with which multiple facets can be bound to a single target.

The label and edit field facets are both attached in newBasicPanelFacets to the same targeter, so they share the same STextual target. Because it is the target that represents the data to be exposed in the surface, after each retargeting both facets automatically display the latest state of that data.

Handling input

A more subtle advantage demonstrated by HelloField is the complete absence of code that explicitly updates the target and facets after input. To understand why this is not required, let's consider how input is handled by the text field facet.

When the GUI is created by the applet host, the facet constructs its JPanel with child JLabel and JTextField components. Setting properties as required to match the title and text state of its STextual, it then listens for input on the JTextField.

As input arrives, the facet can respond appropriately to signals such as Esc and Enter, and ask the Coupler attached to the STextual to validate the current text in the JTextField (which is why it rejects blank texts). Above all, it can decide at some point to update its target with the current text, and trigger a retargeting.

The retargeting

Whenever a retargeting is triggered, the framework takes over. First all targeters are retargeted on appropriate targets. This has no particular effect in a simple application such as HelloField, but is crucial when there may be a selection change.

Next all facets are retargeted on the latest targets of their targeters, and have the opportunity to update their widgets to match the current state and policy of these targets. Facets that share a target will set their widgets to the same state, in effect binding them all to the same data.

Exactly when the text field facet triggers a retargeting depends on a flag set by the Coupler.

The coupler

Both facets and application generally require more than the core functionality of simple STargets such as STextual.

  • The facet may need to know how it should display or update the data represented by the STarget. For instance in HelloField the edit field facet needs to know if it should wait for Enter before committing edits and updating its target.
  • The application often needs to know when a STarget has been updated. In HelloField no further action is required once the edit field facet updates its STextual target. But most applications will need to update data that their targets represent, or (like the next few applets on this page) take some other action..

The Facets implementation of Superficial uses appropriate Couplers to provide this extra functionality. Because couplers are parameter objects that hold no reference to any particular STarget, they can be defined independently of their STargets and shared between logically related STargets.

The larger part of the code related to simple STargets is contained within subclasses of default implementations such as STextual.Coupler; only in a simple case such as HelloLabel can a default implementation be used 'as-is'.

In HelloField the Coupler is subclassed to make the text field facet behave in a non-standard way. By default the facet waits for Enter as a signal to commit, but because updateInterim in the coupler is reimplemented to return true, the facet triggers a retargeting every time that input pauses - this is how the label state tracks the edit state so closely.

Flags, menus

Now that we've covered the basics of creating a Superficial surface that responds to input, let's add some more logic and GUI. HelloSpaces extends the functionality of HelloField by adding a checkbox in the panel and a single-item menu, both defining validation of the greeting text.


Looking at the code of HelloSpaces you will see the following developments compared with HelloField.

  • There are now two targets, one of them of a SToggling providing the validation flag.
  • The Couplers for both targets override several methods so as to provide the necessary logic.
  • A further method from CodingApplet is implemented to define a menu item.

The flag

The validation flag is provided by a SToggling, which is a STarget that represents Boolean values. Like STextual it is constructed from a title text and its own flavour of Coupler, but with its state set and returned as a boolean instead of a String text.

Compared with STextual, SToggling can be exposed by a large variety of facets. Targeters of STogglings can be passed to togglingX methods of FacetsFactory to create facets which define and manage checkboxes and toggle buttons, menu items and even multi-selection lists.

The couplers

HelloSpaces provides a typical example of coding with couplers.

  • Couplers are ideal anonymous subclasses. They can be instantiated as class or method variables, or as parameter objects if they're not shared.
  • They often refer to targets instantiated after them, so to avoid compiler errors targets have often to be defined as instance variables.

Sometimes even the order of instantiation of the targets is critical: in HelloSpaces the SToggling must be created first because isValidText in the STextual.Coupler references it when the STextual is created.

The two couplers provide examples of the two main reasons for overriding default implementations: the SToggling.Coupler responds to state change, while the STextual.Coupler supplies special policy.

Responding to change

If the SToggling is updated by one of its exposing facets so as to disallow spaces, the existing state of the STextual may become invalid.

The SToggling.Coupler allows for this by overriding the default (empty) implementation of stateSet, which is called whenever a SToggling to which it is attached is updated. The new implementation responds to resetting of the flag by fetching the current state of the STextual, stripping any spaces and updating the STextual with the stripped text.

Modifying policy

Whenever the state of a STextual is set, it calls isValidText in the coupler (including at construction). Because an exception is thrown if the return is false, the text field facet uses the same method to pre-check text before updating its target.

The STextual.Coupler extends the default implementation of isValidText which rejects a blank text, adding a check for spaces if required by the state of the SToggling.

The menu

Reimplementing newBasicMenuFacets to return a non-null array signals to the superclass CodingApplet that it should construct a menu bar (inside the panel!), adding to it menu items for each of the facets returned.

In HelloSpaces a single SFacet is returned, created using togglingCheckboxMenuItems. Because the facet is attached to the same targeter as the panel checkbox facet, the framework ensures that their widgets are synchronised just like the panel label and text field.

Buttons, disabling

HelloCommit is a another version of HelloField which introduces another new STarget type. It extends the functionality of HelloField with logic that commits or cancels edits to the greeting text, exposed both by buttons in the panel and items in the menu. Buttons and menu items are disabled unless the actions they represent are meaningful.


The code of HelloCommit shows the following developments compared with HelloField and HelloSpaces.

  • A second STextual holds uncommitted edits to the greeting text.
  • Two STriggers represent the actions of committing or cancelling edits.
  • Each pair of targets shares a custom coupler instance.
  • The logic provides for disabling of buttons and menu items.

The textuals

The two STextuals are constructed from a shared custom STextual.Coupler instance, which overrides updateInterim as before (this will only be called for edits). It also overrides textSet which is called whenever the state is set of either edits (usually by its text field facet) or greeting (only by the STrigger.Coupler); in both cases it calls the convenience method setTriggerLives described below.

The triggers

STrigger is a stateless STarget that represents an application action or process, exposed by either buttons or simple menu items. STriggers are constructed with a Coupler that has a single fired method, which here checks whether commit or cancel has been fired and copies the state of one of the STextuals to the other as appropriate.

Enabling and disabling

Superficial makes it easy to manage the enabled state of GUI widgets. Every STarget returns a live value that can be checked at each retargeting by its exposing facets and used to disable/enable the appropriate widgets.

The live state can be set for any STarget, but it only returns true when any enclosing groups (see below) are also live; this facilitates very fine-grained control to the enabled state of the GUI.

Grouping

The commit and cancel STriggers are returned from newBasicTargets wrapped in the commitCancel STarget group. Grouping allows logically related targets to be treated by the framework as one, and HelloCommit takes advantage of it in two ways:

  • From the single targeter exposing commitCancel, the FacetsFactory can construct with triggerButtons a single facet that constructs and manages a panel of equally-sized buttons, and from triggerMenuItems another that creates corresponding menu items.
  • All the buttons and menu items can be enabled or disabled at once by setting the live state of commitCancel in the setTriggerLives convenience method.

Handling numbers

HelloLimit is very similar to HelloSpaces, but restricts the maximum length of the greeting text to a limit exposed in several different ways.


The code of HelloLimit is essentially that of HelloSpaces, updated to set a different constraint.

  • The text of the STextual is limited in length.
  • The limit is defined by a SNumeric which in turn is constrained by the NumberPolicy returned by its Coupler.
  • The SNumeric is the shared target of three facets in the applet panel and menu, one exposing its state in two different ways.

The number

The length limit of the greeting represented by the STextual is itself represented (as a double value) by a SNumeric. When its value changes, valueSet is called in the Coupler, here implemented to ensure the text length does not exceed the new limit.

Similarly, the STextual.Coupler overrides isValidText to check the length of the text proposed against the current limit.

The policy

SNumeric holds a double of potentially any value. How can you ensure that it is constrained in the GUI to an integer value within a sensible range?

The SNumeric.Coupler returns the necessary validation policy as a NumberPolicy initialised with the minimum and maximum limits to be set. In HelloLimit a Ticked is returned, which extends NumberPolicy to supply the display policy required by a slider facet to define its ticks and labels. Re-implementing unit constrains both slider and numeric field to allow entry only of multiples of 5 in the defined range, and sets the size of the nudge for the panel buttons and menu items.

The facets

The facets created in newBasicPanelFacets are broadly similar to those in HelloSpaces, and demonstrate further the power of the facet concept, not to speak of the flexibility and concision of the FacetsFactory interface.

The textual facets are exactly as before, the spacer facet and BREAK constant together add a blank row to the panel.

Contrary to what you might expect, there just two numeric facets (separated by a horizontal spacer facet).

  • The HINT constants passed to numericSliders ensure that the facet returned not only creates a titled, labelled and ticked slider to meet the NumberPolicy supplied by the Coupler of its SNumeric target, but also adds a numeric field and arranges its widgets in two rows.
  • The facet obtained from numericNudgeButtons is a rare example of a GUI element providing control without view (almost). Once again following the NumberPolicy, its paired buttons increment and decrement the value where possible, becoming disabled when the value is at the end of its range.

The single facet returned by newBasicMenuFacets creates a pair of menu items corresponding to the panel nudge buttons, then groups them in a sub-menu.

Choosing from a list

HelloChoose differs from applets such as HelloSpaces in that it does not allow its greeting to be edited, but instead presents a choice of texts using the last of the simple STarget types.


The code of HelloChoose is simpler than HelloSpaces.

  • An SIndexing represents a list of possible texts and an index into that list.
  • The state of the STextual is updated whenever indexSet is called in the SIndexing.Coupler.
  • The SIndexing is exposed by a total of five panel and menu facets, creating and managing two or three widgets each.

Editing a selection

HelloSelect both allows the greeting to be edited as in HelloField, and presents a choice of texts like HelloChoose.


As you might expect, the facet details in HelloSelect resemble those for HelloField and HelloChoose. But otherwise the code presents several puzzles.

  • Both targets and facets are defined within different methods from the previous applets.
  • The STextual is defined within a new SFrameTarget type.
  • No SIndexing is defined, but a suitable STargeter is passed to the facet methods.

The methods

The key to the different methods lies in the constructor to HelloSelect, which creates an array of HelloTexts and passes it to the superclass. This triggers a radical change in the behaviour of CodingApplet because it now has to allow editing of a selection within the content supplied.

Just creating simple targets in newBasicTargets would not handle this new complexity, which is why in HelloSelect it returns an empty array. Instead, CodingApplet uses the framework to construct elements (including the missing SIndexing) that allow selection within the content array.

In HelloSelect each content selection is passed to newSelectionFrame to allow targets to be created representing its editable state; the initial targets together with the hidden SIndexing are used to create the extra STargeters passed to newContentPanelFacets and newContentMenuFacets.

The frame

On the face of it, newSelectionFrame might just return an array of STargets like newBasicTargets. However the framework requires targets representing a selection to be wrapped by a distinct subclass of SFrameTarget (which makes possible advanced features like adapting the GUI to match the current selection).

SFrameTarget extends the basic STarget implementation not by specialising for a specific content type, but by exposing its content directly as an immutable framed member. While this is of little benefit in HelloSelect, it allows more complex applications to access the selected content via the framework.

To ensure subclassing, SFrameTarget cannot be constructed from its STarget child elements; instead it creates them during the initial retargeting using lazyElements. In HelloSelect this returns a STextual much like that returned by newBasicTargets in HelloField, with the important difference that its Coupler reimplements textSet to update the state of the selected HelloText.

The facets

The main development in the facet creation methods is in the parameters passed.

  • basics are targeted as before on the targets returned by newBasicTargets (so the array is empty in HelloSelect).
  • indexing is targeted on the SIndexing created by CodingApplet and allows view and control of the index into the content array.
  • selection is targeted on the SFrameTarget returned by newSelectionFrame, so the first member of its elements is targeted on the STextual.

Though the STargeter parameters have STargets created in different places (including one in the superclass), the exposing SFacets can be freely arranged in any combination.

Bringing it all together

HelloAll combines the functionality of all previous applets on this page:


HelloAll
therefore needs very little new code, but does have a few points of interest.

  • Non-selection targets are created in newBasicTargets.
  • The Coupler shared by the triggers can access the current selection using a method from CodingApplet.
  • Some of the SFacets created in newContentPanelFacets are arranged within a nested panel.

Where next?

Try downloading the source code of real-world Facets applications and see how you get on.

 

© 2011 David M Wright