jMonkey LorisGUI - XML

Leveraging XML configuration

Home Overview Visuals XML Style Model Controls Components Layouts
 

Extending Savable

The jme Savable mechanism provides a robust mechanism for defining jme constructs within XML. Loris utilizes the extensions defined in the jMonkeyCSG XMLLoader class to make the xml definition process a bit more human friendly. The basic approach remains the same. The xml file contains the declaration of some Savable, identified by its class. Attributes are matched by name and are required to be of an expected type. A nested Savable can be declared as a child DOM element, matching by name like an attribute.

The com.jme3.export.xml.XMLLoader class extends JmeImporter, and operates with a com.jme3.export.xml.XMLInputCapsule to act as the DOM reader. XMLLoader provides a shorthand notation that maps a nested Savable element name to the desired class. This makes the xml much more concise and easy to read.

XMLLoader supports string substitution from ResourceBundles, which is great for internationalization. A string construct of the form '${bundlename.propertyname}' within the xml will be replaced by whatever is found mapped to the entry 'propertyname' within the registered ResourceBundle 'bundlename'.

XMLLoader extends the 'id=/ref=' mechanism. This mechanism allows an item defined in one part of the xml to be reused in a different part. When 'ref=' is used, the original item that declared 'id=' becomes the Savable, and no other processing takes place. With XMLLoader, you can replace 'ref=' with 'clone='. In this case, a clone is made of the original 'id=' element, and any other attributes declared within the xml are applied to the cloned copy.

Another extension is the com.jme3.export.xml.XMLContextKey class, which is a subclass of jme AssetKey. The XMLContextKey.read() method detects that it is being called by an XMLLoader. In that case, it acts as both a standard AssetKey and retains the active 'context' within the current XMLLoader. So if an Asset is then loaded from this XMLContextKey, previous definitions for "id=" and ResourceBundle substitutions remain in effect. This makes a nested Asset load from within the .read() processing of another Savable appear very transparent. It facilitates breaking up large XML configuration files into simpler, smaller subfiles.

To make use of XMLLoader, you need to register it (typically very early in your processing, often within .simpleInitApp() )

        
    assetManager.registerLoader( com.jme3.export.xml.XMLLoader.class, "xml" );
	    
 

Loris Setup

A typical use of the xml configuration for Loris is to define everything you need in a single config file. Loris can then be initialized within .simpleInitApp() as follows:

    GuiContext aTemplate = new GuiContext( this.getGuiViewPort(), this.guiNode, true, true );
    GuiGlobals theGui = GuiGlobals.initialize( this, "myconfigfile.xml", aTemplate );
	    

A very simple configuration file would look something like (sample from test harness):

<?xml version='1.0' encoding='UTF-8'?>
<globals>
    <root>
        <elements>
            <label text='Just Some Text'  font='Interface/Fonts/Default.fnt'>
                <textColor r='0.85' g='0.5' b='0.5' a='1.0'/>
                <insets position='Center'/>
            </label>
        </elements>
    </root>
</globals>
	    

This example defines an active 'root' element (which defaults to a Screen) with a single GUI item, a 'label' that says "Just Some Text" which is centered. Since you have defined a 'root', your screen is automatically loaded and displayed via the jme standard GUI viewport.

To make ResourceBudles available for string substitution, you must start with an XMLContextKey (as provided by GuiGlobals) and register your bundles with it before passing it into the initialization call:

    XMLContextKey aConfigKey = GuiGlobals.prepareXMLContextKey( "myconfigfile.xml", null );
    aKey.registerTranslationBundle( "MyMsgs", ResourceBundle getBundle( "Config.MyApp.Messages" ) );
	
    GuiContext aTemplate = new GuiContext( this.getGuiViewPort(), this.guiNode, true, true );
    GuiGlobals theGui = GuiGlobals.initialize( this, aConfigKey, aTemplate );
	    

Any string references within the XML of the form ${MyMsgs.MessageOne} will lookup the property "MessageOne" in the given bundle and substitute it. You can register any number of different resource bundles, using different reference names.

 

Style Sheets

XML is also used to define the CSS-like style sheets used to control the appearance of the Loris controls. You can define any number of different sheets, and declare one as the default. Every control can explicitly call out by name to use a specific sheet, or it can say nothing and use the default.

Sheets associate a selector with a set of attributes. Selectors can also nest within other selectors. The xml looks something like:

<?xml version='1.0' encoding='UTF-8'?>
<globals>
    <styles defaultStyle='TestSheet'>
        <styleSheet styleID='TestSheet'>
            <!-- A selector without an id defines generic values used when nothing else applies -->
            <selector>
                <attributes>
                    <font value='Interface/Fonts/Default.fnt' />
                    <textColor r='0.5' g='0.5' b='0.5' a='1.0' />
                </attributes>
            </selector>
            
            <!-- This selector applies to Buttons, and changes the text color -->
            <selector id='Button'>
                <attributes>
                    <textColor r='0.85' g='0.2' b='0.2' a='1.0' />
                </attributes>
            </selector>
        </styleSheet>
    </styles>
</globals>
    	
 

Attributes

You will notice that attributes within the style sheets closely match the attributes used within the Savable definitions. This is on purpose to make it as easy as possible to declare the visual appearance either through style settings or via per-element configuration. When an attribute is a Savable, the declarations should be identical. Notice the above use of <textColor> in both the style sheet selector and within the <label> declaration. For non-Savables, the attributes in a selector use the construct <font value='xxx'/>, while the attributes in the <label> use a simpler font='xxx' construct.

Anything you can set via a selector attribute, you should be able to set in a direct XML definition. The reverse, however, is not true. Some attributes only make sense in the XML definition and are absurd as styling. For example, the XML declarations support the elementID= and elementSubID=. Since both elementID and the subID are used to select which style to apply, it is meaningless to try to define them within the style itself. Similarly, no case of style-based parent/child relationships has been encountered yet, so the contents of layouts cannot be defined via styling.

 

Definitions

Both <globals> and <styleSheet> support the notion of <defintions>. Definitions contain a set of items used elsewhere in the XML processing. Each item is either:

  • <replace key='propertyname' value='stringsubstitution'> which leverages an internal ResourceBundle for substituting a string of the form &{propertyname}.
  • <somesavable id='referenceID'> which defines an instance of some Savable which can thereafter be referenced by <someElement ref='referenceID'/>