Skip to content
Daniel Gerson edited this page Aug 31, 2013 · 33 revisions

TriplePlay uses PlayN’s API so make sure you’re familiar with the various rendering types before continuing. ( src: https://developers.google.com/playn/devguide/rendering )

The TriplePlay UI system is designed to provide standardized widgets and layouts for UI interaction, while preserving and utilizing PlayN’s game loop to achieve this goal. As such, the UI system will usually be kicked off as follows:

  • Create a tripleplay.ui.Interface instance.
  • Use this interface instance to create various root elements
  • Add a number of tripleplay.ui.Group instances to the root as a hierarchy.
  • Add various widgets as the lowest elements in the hierarchy.

##Understanding tripleplay.ui.Root The Root element represents the top of a discrete hierarchy and can be thought of similarly to the RootPanel in GWT. The Root acts simlarly to the Group below in that it also operates with a provided Layout policy.

The root will intercept all pointer events in its bounds, but it shouldn't mess with pointer events outside its bounds. Call root.pack() to pack the max bounds for interception.

##Understanding tripleplay.ui.Interface The interface object is needed to marshal all the time related “events” of all the root panels it’s created and for all of their children. As such, it provides an Animator instance (refer to above) that all the sub elements will use for their animations(see tripleplay.ui.Menu for an example), and also provides a similar style Task mechanism that elements can use to make use of the update-loop until such time as the Task completes.

Are you supposed to only create one interface? Ideally yes, but if you have separate parts of your PlayN game or have packaged a library to be used in other games that needed control of its own interface, then it's conceivable to have more.

##Understanding tripleplay.ui.Group : As is described in the documentation, the Group element is used for containing other elements and laying them out according to a layout-policy.

##Making sense of Style, Styles and Stylesheet : The styles mechanism in tripleplay is similar, but not exactly the same as CSS. Every property of an element that is considered a style of that element, is represented by an instance of the Style class. The sum collection of styles that happen to be set for an element is represented by an instance of Styles. A collection of Styles for various classes and modes of elements is represented by a Stylesheet (not for various instances of elements, there is currently no equivalent of CSS selectors).

Implementation note: All the styles for a particular element are calculated when its LayoutData is
calculated. This traverses up the element hierarchy seeing which styles are applied to the widget
in question. Actually this happens as the LayoutData instance is created, pre-constructor. So 
for a Button this would happen when it's TextLayoutData is created.

Your actual decisions for what styles to use are stored as bindings.

//pseudocode
Style SHINEY_COLOUR = new Style<Integer>()
...
int red = ColorUtil.rgb(255,0,0); //red
mywidget.getStyles().add(new Style.Binding<Integer>(SHINEY_COLOUR, red)); //WRONG!!!!
//or more easily as
mywidget.getStyles().add(SHINEY_COLOUR.is(red));  //EQUALLY WRONG!!!

Well, that would have been the way to register a binding, except that .getStyles() returns the Styles object which is essentially immutable, so calls to .addXXXX will return a new Styles object leaving the original intact. There is a rather meek warning in the comments The receiver is not modified.

So one of the right ways to do it is more like

mywidget.addStyles(Styles.make(SHINEY_COLOUR.is(red)));
//or to add a different colour when selected simulateously
mywidget.addStyles(Styles.make(SHINEY_COLOUR.is(red))
	.addSelected(SHINEY_COLOUR.is(blue)));

Remember that an element can have different styles associated with different modes (SELECTED, DISABLED, etc). In order to find the right binding at rendering/creation time, TP has to produce a second binding: Styles.Binding (plural), in order to store all the separate individual Style.Binding's listed.

##Understanding the layout

See this page for a walkthrough of the flow of what happens in TPUI under the hood.

Common Tasks

Transparent Backgrounds

Stackoverflow link

###Changing the colour of font buttons

Buttons inherit from TextWidget. TextWidget's define a Style COLOR, so setting the text colour is as easy as

button.addStyles(Styles.make(Style.COLOR.is(mycolour)));

###Changing the font size of a button, and adding font drop shadow

Again, Button inherits from TextWidget, TextWidget has a TextLayoutData to handle the final instantiation of its styles and other layout data. TextLayoutData is responsible for doing cool things with text. Its two main responsibilities are for executing a TextFormat and an EffectRenderer. These aren't classes, they are conceptual processes that happen on the text. The text format consists chiefly of the style Style.FONT. Given that the rendering of text happens by the underlying framework, it's convenient to have the FONT represent by the typefont and the size in one go like so

.add(Style.FONT.is(PlayN.graphics().createFont("Helvetica", Font.Style.PLAIN, 16)));

The dropshadow would be created by

.add(Style.TEXT_EFFECT.shadow)
.add(Style.SHADOW.is(0x20000000)).add(Style.SHADOW_X.is(1f)).add(Style.SHADOW_Y.is(1f)))

TEXT_EFFECT enables the effect, the rest determine the colour and offsets of the shadow.

More text effects can be seen by looking at the Style class.

Clone this wiki locally