Styled UI - coldrockgames/gml-raptor GitHub Wiki

At the starting page of the UI Subsystem, the style-array is mentioned as one of four ways to create your UI.

This page takes a closer look at this feature.

The style-array Format

A style-array looks like this:

[
  [ Panel, { min_height: 144, dock: dock.top, padding: 12 } ,
      [ Label, struct_join(label_struct, { text: "[scale,1.5][ci_accent]UTE - raptor Unit Test Engine", position: [48,32]}) ],
      [ Label, struct_join(label_struct, { text: "[scale,1.5]Discovered Test Suites", position: [48,84]}) ],
      [ Label, struct_join(label_struct, { text: "[scale,1.5]Detailled Test Results", position: [968, 84]}) ],
  ],

  [ ScrollPanel, struct_join(scrpanel_struct, { 
      min_width:         1000, 
      dock:              dock.right,
      content_autofill:  [true, 4],
      content_object:    [UnitTestResultsViewer, { detail_mode: true, }]
    })
  ],

  [ ScrollPanel, struct_join(scrpanel_struct, { 
      dock:              dock.fill,
      content_autofill:  [true, 4],
      content_object:    [UnitTestResultsViewer, { detail_mode: false, }]
    })
  ]
]

Let's go through this, step by step.

  • It must be an array (and not a struct) because the order matters and structs can not guarantee to have a constant order of items.
  • You always create one single array which contains several sub-arrays. Speaking of json or GML, you always create
    [ ...content... ].
  • Each of the sub-arrays follows this structure:
    • [ type_to_create, <style_struct> ] for any non-container item (like the Label objects above) or
    • [ type_to_create, <style_struct>, [ next-level-subarray], ... ] ] for containers (like the Panel above, which contains the Labels)
  • So you create any hierarchy in the form of array containing a type, init/style structs and another inner array for the elements of the container

Advantages of this approach

  • You do not have to deal with the ControlTree itself, no need to care about when and what to .build(), no need to call all those functions.
  • Just pure content, pure data.
  • Highly dynamic, as you could even build such an array at runtime of your game and create dynamic UIs (like windows for inventories and similar things) specialized for the view you want to show.
  • It can be "put together" like Lego pieces at runtime.
  • It is compatible with the json format, so you can define your UI freely in your game configuration files, hence you could even "drop" parts of those array from your Race query to create custom details/properties views based on the dropped item from a loot table.

A Technical Look

Behind the scenes, raptor executes a function which iterates recursively through this array and does all the .add_control(...) and .build() calls for you.

On every ] end-of-array, if it contained sub-arrays (i.e. if it was a container), .build() is invoked to make sure, the container has done all the calculations.

When reaching the end of the top-level array, .build() is invoked on the master container, finishing the UI layout process.

With this feature, you can create UIs without any knowledge about the ControlTree itself, aside of knowing the functions, that are supported for the style_struct.

Public Functions That Use The style-array

Raptor offers two public functions, that will render this style-array for you:

/// @func	styled_ui_create_room(_style_array)
/// @desc	Submit a style_array to the UI_ROOT for the
///		main ui of the current room.
///		UI_ROOT.clear_children() is invoked, before the
///		array gets applied.
///		This function always returns the UI_ROOT for chainability.
function styled_ui_create_room(_style_array) {
/// @func	styled_ui_create(_container_or_tree, _layer_or_depth, _init_struct, _style_array)
/// @desc	Submit a style_array to any styleable control (i.e. any _baseContainerControl).
///			You can create panels or windows or any other container.
///			instance_create(...) is called for you and the created control will be returned.
function styled_ui_create(_container_or_tree, _layer_or_depth, _init_struct, _style_array) {

Style-Struct Functions

In short terms, every set_* function in ControlTree can be used as a property in the style-struct. You do not have to write "set_". When you write dock: dock.fill, raptor translates this into a call to set_dock(dock.fill).

Note

In addition to the calls in the table below, you may add any of the Variable Definitions, the control has. Examples are text, font_to_use, text_color, etc. Just look in the inspector of the variables for any of the controls to find out, what you can style here.
To be exact, even if you write a variable here, that does not exist yet, it will be created in the instance! If you write my_cool_variable: true in the style struct, your instance will have this as a member variable.

Available commands in a standard ControlTree

Command Arguments Description
margin left_or_all,
top=undefined,
right=undefined,
bottom=undefined
Define the margin (outer distance) between controls. If you supply only one argument, it is applied to all four directions.
margin_left margin Set the left margin.
margin_top margin Set the top margin.
margin_right margin Set the right margin.
margin_bottom margin Set the bottom margin.
padding left_or_all,
top=undefined,
right=undefined,
bottom=undefined
Define the padding (inner distance) between a control and the border of its render area. If you supply only one argument, it is applied to all four directions.
padding_left padding Set the left padding.
padding_top padding Set the top padding.
padding_right padding Set the right padding.
padding_bottom padding Set the bottom padding.
position xpos,
ypos,
relative=false
Set a fixed position.
NOTE: Padding and Margin are ignored when using static coordinates!
position_from_align valign,
halign,
xoffset=0,
yoffset=0
Sets the position, a control would have if aligned as specified as static coordinates.
NOTE: Padding and Margin are ignored when using static coordinates!
spread spreadx=-1,
spready=-1
Both arguments range from 0..1 meaning percent of the total width/height. If you set spreadx to 0.5 this means "50% of the width of my container".
dock dock Apply docking to the control. Docking on a side means (let's take "left" as an example): "Take the full height of my container and set my x-position to zero." So it's positioned on the left edge of the container and takes the full height.
dock.fill uses the entire remaining space of a container in all four directions.
reorder_docks reorder True by default. Reorder dock means a more "natural" feeling of adding right- and bottom docked elements. When you design a form, you think "left-to-right" and "top-to-bottom", so you likely want to have the second bottom added to appear BENEATH the first bottom! If you do not want that, just turn reorder off.
align valign=fa_top,
halign=fa_left,
xoffset=0,
yoffset=0
Align the control in its container in the same way you would align text in a Button or Label. fa_top/fa_middle/fa_bottom are used for vertical alignment and fa_left/fa_center/fa_right for horizontal alignment.
anchor anchor Perform anchoring on a control in its container. An anchor means, the distance from the edge of the container to the control will stay constant. So if the container changes its size, the control will change its size too, but will keep the distance to edge constant.
NOTE: This is a bit-flag. You may use binary or operator to set multiple anchors, like anchor.top or anchor.bottom
name name Set the name of the control. It can be accessed later on through the ControlTree by calling get_element(name).
visible visible Set the visible flag.
NOTE: This will set visible recursively on all child controls too!
enabled enabled Set the enabled flag.
NOTE: This will set enabled recursively on all child controls too!

Additional Commands available in a ScrollPanelControlTree

Command Arguments Description
content_object object_type,
init_struct=undefined,
custom_draw_method=undefined
Define the content object of a ScrollPanel. The custom_draw_method may be a string (the name of the function), if the control shall use something else than draw_self to draw its contents. This function must exist in the control and is invoked when the gpu_scissor is active to draw parts of the content.
content_autofill autofill,
inner_padding
By default, autofill is true for content objects. This flag means, that the ScrollPanel will resize the content object to take up all the available visible space of the panel, if its size is smaller than the panel size.
⚠️ **GitHub.com Fallback** ⚠️