-
Notifications
You must be signed in to change notification settings - Fork 25
Basics
Note: all examples on this and other tutorial pages are using 1.6+
version syntax, with :
as macro marker. Older version feature @
as macro sign and might not fully support some of the mentioned features. Please, use the latest official version of the library or snapshot release.
LML is a HTML-inspired markup language designed to be parsed to Scene2D
widgets, allowing you to quickly create your views. Thanks to powerful macros, you this is not just a simple data format (like Android UI) - you can use loops, conditionals, iterations and other tools to easily customize your templates.
LML uses a XML-like structure. While LML templates can be valid XML, the parser is much more forgiving. Your file does not have to use a single root tag or quotations in attributes. In all examples below, the default syntax characters from version 1.6+
are going to be used.
In LML, you work on tags and its attributes. This is a simple tag example:
<tag attribute=value>
Quotations in assignments are optional, but if you really want to pass these, you can:
<tag attribute="value">
<tag attribute='value'>
These two will evaluate to the same result as the tag in the first example.
Each tag is converted to a Scene2D
actor, usually with the same name (although there are many alliases - and you can assign your own as you see fit). You create LML views by constructing structures of tags. Each tag can be parental or a child:
<parent>...</parent>
<child/>
Child tags are simple actors that can be fully constructed and customized with their tag attributes. They do not depend on any children or need them to work properly. An example might be a slider:
<slider min=0 max=100 value=50 stepSize=0.1 animateDuration=0.2 />
Sliders do not extend Group
, so they cannot append any actors, and there is no reason for sliders to parse plain text between their tags.
Widgets like sliders are usually in child tags. As you might guess, attributes are either values used to create the widget (constructor arguments) or simple properties (set through public setter methods). Note that there are no attributes required to create any widget: default values will be used instead. For example:
<slider/>
<label/>
<textButton/>
These tags would create a slider with default min, max and step size, label with no text and empty text button, all using default skin and styles. Default LML tags will never fail to create an actor because of lack of an attribute.
Parent tags, on the other hand, are prepared to parse text or children actors. Or both.
Parental tags decide how plain text and other tags inside them are parsed and appended. Note that overlapping parental tags throw exceptions - LML follows XML rules about closing tags - the last tag that was opened has to be closed first. Since the parser expects that you will close the last tag you opened, this cannot be done:
<!-- Throws exception! -->
<label><button>This is not OK.</label></button>
Some widgets (for example: our slider) cannot have nested tags or text simply because they cannot do anything with them - a strict parser (default setting) will throw an exception. Others, like label, can only parse plain text:
<label>Content.</label>
<!-- Throws exception! -->
<label><button/></label>
The first label tag will be converted to a Label with "Content." text. The second will throw an exception, since Label
actor is not a Group
and cannot add other widgets.
Most of the other widgets, however, have a way of appending others. The most commonly used group actor is, of course, Table
(which is also extended by all buttons and windows):
<table>
<label>Some label</label>
Plain text
<textButton>Some button</textButton>
</table>
As one might expect, Table
will append children tags to its cells (Table#add(Actor)
). It also converts plain text to Label
actors with default style and adds them like to itself, which is also common behavior for most other group tags. Parents can add additional attributes to their children:
<label pad=10>Will not work, not in a table</label>
<table>
<label pad=10>Will be padded</label>
</table>
If the actor has no direct Table
parent (or one of its extensions), including "pad" attribute will most likely cause an exception, as the attribute will be unknown. If the actor is in a table, "pad" will be properly parsed and call Cell#pad(Value)
method. Having a Table
parent adds a number of additional attributes that can be used to customize the look of your view.
As a rule of thumb: if there is a setter for a property in an actor class, its tag will be able to parse attribute with a similar name (stripping "set"). For example, since Group has #setTransform(boolean)
method, all Group
-extending actor tags can have "transform" attribute that expect a boolean.
If an actor contains component actors of a different type, it can also access its attributes. For example, both TextButton
and Window
manage an internal Label
- if you pass Label
attributes to their tags, they will be recognized and used to modify the internal Label
that they use.
There are some exceptions to the naming convention, however. If the setter method name collides with an attribute name that can be given by a parent, these methods are usually assigned to different attributes. For example, both Table
and Cell
have #pad(Value)
method - and since we want to be able to use all Table
methods even if it is nested inside another table, "tablePad" attribute is used for Table#pad(Value)
method instead:
<table>
<table tablePad=4 pad=4>...</table>
</table>
In this example, the first attribute will set Table
's padding and the second with set the padding of table's Cell
object (of its parent).
You can use spaces in tags and attributes, but they need to be in quotations, as spaces are normally used to separate tag entities. For example, "title" will be properly parsed as one attribute in this template:
<dialog title="Some complex title"/>
There are some additional attributes that can be parsed by all actors, but are not normally available through Scene2D API. These include (but are not limited to):
-
id
: sets actor's unique ID usingActor#setName(String)
method. Normally, default parsing method returns an array of actors that were roots (had no parent tags). However, you might want to access some specific child actor and that's when this attribute comes in. IDs can be used to access (or inject) actors after the parsing: you can name the actor in the LML template and get direct reference to its constructed object by invokingLmlParser#getActorsMappedByIds()
after parsing. By default, IDs are compared ignoring case, so make sure not to set colliding IDs. (The returned map also ignores case - "KEY" will return same value as "key".) -
style
,class
: chooses the name of the style defined in theSkin
which will be used to construct the actor. -
skin
: allows to choose the ID of Skin used to create the widget. LML parser supports multiple skins at once and you can choose which one to apply. If not specified, default registered skin is used.
For example:
<table id=root>
<label id=someId skin=custom style=big>Special label.</label>
</table>
Normally, LmlParser#parseTemplate(String)
would return an array containing only the Table
widget (with root
ID), as it is the only root tag in this template. However, Label
can be accessed by getting the ID-to-actor map (LmlParser#getActorsMappedByIds()
) and using #get(String)
method with "someId"
argument. The label will be constructed with LabelStyle
named "big", present in Skin
mapped to "custom".
You can go through all LML attributes and tags in the source of DefaultLmlSyntax
or by checking docs of attributes (they end with -LmlAttribute
) and tags (ending with -LmlTag
). DTD support is also provided.
MJ 2016