Skip to content

Boo User Interface (BUI)

Warning

BUI is still supported in Empeld, and necessary as most of its core interfaces use it. However, it can be tricky to get started with, so we recommend ui2 as it's more reactive and bound to a context (A little vuejs/angular style, if you're familiar)

BUI’s mentality is an HTML-like interface connecting game logic with front-end appearance. It uses an XML markup to describe widgets, just like HTML, and a powerful scripting language (Boo, which is like Python), to modulate the appearance. You can modify, generate, remove, move, and animate widgets completely in Boo.

Code Docs

If you're looking for Widget documentation, see here: Widgets

Basic Layout

The basic layout of a BML file is:

Here's a very simple example, pulled from the chat UI:

<control>
    <script src="chatroom.boo" />

    <style>
        <class name="messagebox" inherit="textbox">
            <property name="BackgroundColor" value="#ddd1" />
            <property name="BackgroundActivated" value="#ddde" />
        </class>
        <class name="messagequeue" inherit="terminal">
            <property name="HasBackground" value="false" />
            <property name="HasBorder" value="false" />
        </class>
        <class name="sendbutton" inherit="button">
            <property name="BackgroundColor" value="#fff3" />
            <property name="BorderColor" value="#6663" />
        </class>
    </style>

    <view VAlign="Bottom">
        <container width="475" Height="300" FitToParent="False">
            <terminal X="0" Y="0" Width="475" Height="275" id="term" ShowExpiredMessages="False" MaxLines="500" class="messagequeue" />
            <textbox X="0" Y="275" Width="400" Height="25" id="message" class="messagebox" Placeholder="Type '/'" />
            <button X="400" Y="275" Width="75" Height="25" Text="Send" id="send" class="sendbutton" />
        </container>
    </view>

</control>

The important part of the BML is the control, script, and view. (Think HTML, Head, and body; respectively)

Text Formatting

There are currently a few built-in text formatting options. Each option starts with a ^ character, followed by a command character, and ends with a ;. Here are the list of each and what they do:

Command Example Description
\ \^asdf; \ is the escape character, and will force-write anything after it without special interpretation
# ^#0F0; Set the color of the following text using CSS 3 or 4(Alpha) digit hex colors. This case is green
@ ^@16; Set the font size to, eg, 16
x ^x0; Set the x coord to 0, back to the left-hand side of the text block
y ^y23; Set the y coord of the block to, eg, 23
> ^>-12; Offset the x coord by -12
| ^|20; Offset the y coord by, eg, 20

Development

On Jan 27 2015, we introduced a UI-development mode in the game, toggable via commandline. This allows you to load up your UI and scripts, and validate that your scripts don't throw any errors, and observe the appearnce. Run it by typing:

empeld.exe --dev-ui /path/to/my/buifile.bml

Whilst in there, it will watch for changes to the bml file and automatically reload.

The following hotkeys are available:

  • F4: Draw guides on the GUI
  • F5: Force-reload the UI file
  • Escape: Exit

Widgets

BML is composed by a set of widgets, each has some common properties, but also have custom properties, fields, and events. You can find all of them described Widgets.

Common widget properties include:

Property Type Description
id string Set the ID of the widget to be referenced from the script
class string Set the name of the style class to retrieve widget styling from
data-* any You can use data-fields to store any extra data that you might want to reference from the Boo script

Image Paths

A common use case specifies a picturebox image path. For technical reasons, these paths can't be relative, they must be absolute. To handle this case we've added a special variable that will return the current markup file's absolute path. eg, if you want to reference a file called "heat.png" in the same folder as the XML, you would do:

<picturebox Image="{path}/heat.png" Width="32" Height="32" />

BUI Glue

BUI Glue was a layer developed to help bind models to UI, without you having to keep track of updating UI state. Here's some example code.

There are 3 major parts:

  • The glued C# model
  • The BOO script that inherits GlueStick
  • The BML file that has bound variables via the bind-* attribute

There are a few custom attributes that it adds to the markup:

Attribute Description
bind-* Bind the value of the attribute to the widget's variable. eg. if bind-Text="var", the parameter Text will be bound to the model variable var
format-* This will do a string.Format on the bound variable before it is set to the bounded widget field. eg. format-Text="Before {0}", will put "Before" before any change on Text field.
transform-* This will pass any bound variable through a method before it is passed to the Widget's field. eg if transform-Text="CustomTransform", it will pass the bound model variable through the boo script's method CustomTransform before inserting into Text.
reconstruct-* This will pass any bound variable through a method before it is passed back up to the application model. eg if a textbox text is '123', and the reconstruct method makes it an int, it will pass it through that before returning to the program
event-*eventname* Will invoke a bound name to an event. eg, on a button you can have event-ButtonClickEvent="doSomething". Then, a method that has been bound code-side will invoke a method with void return and void args "doSomething"

Plugin

public class MyModel{
  [Glue]
  public string MyVar;

  [Glue(Writable = false)]
  public int MyOtherVar{
    get{return 1;}
  }
}

var model = new MyModel();
var control = screen.AddControl("path/to/my/control.bml");
control.GlueModel(model);

model.MyVar = "Hithar"; //The UI will automatically update

BML

<?xml version="1.0" encoding="utf-8"?>
<control>
    <script src="myscript.boo" />

    <view>
        <label x="0" y="0" bind-Text="MyVar" />
        <label x="0" y="30" bind-Text="MyVar" format-Text="Something is before {0}" />
        <progressbar x="0" y="60" Width="100" Height="25" bind-Value="MyOtherVar" transform-Value="customTransform" />
    </view>
</control>

Boo

#include "gluestick.boo"

class Controller(GlueStick):
  def constructor(ui):
    super(ui)

  def customTransform(v):
    return v + 5

Special Boo Methods

Besides the constructor and the destructor defined by Boo, Empeld has a few additional bound methods it will call.

Name Description
init() Called after the entire UI has finished loading. Control does not exist on screen yet
map() Called when the UI is mapped to a parent widget. Doesn't necessarily mean it's visible
unmap() Called when the UI is unmapped to a parent widget.
focus() Called when a widget gains focus
__losefocus() Called when a widget loses focus

Default Boo Controller

If you find you don't need any customer properties, methods, or transforms, you can use the default glue controller by referencing:

<script src="ui/glue.boo" />

Widget XML Mapping

This switch statement defines the mapping of the XML name to the widget type as described in Widgets

switch(name.ToLowerInvariant())
            {
                case "label":
                    return new Label();
                case "wrappedlabel":
                    return new Label(){AllowOverflow = false};
                case "header":
                    return new Label(){Size = 26};
                case "picturebox":
                    return new Picturebox();
                case "bitmap":
                    return new Bitmapbox();
                case "sprite":
                    return new Sprite();
                case "button":
                    return new Button();
                case "checkbox":
                    return new Checkbox();
                case "progressbar":
                    return new ProgressBar();
                case "textbox":
                    return new Textbox();
                case "numberbox":
                    return new Numberbox();
                case "verticalscrollbar":
                    return new VerticalScrollbar();
                case "block":
                    return new Container(){FitToParent = false};
                case "container":
                    return new Container();
                case "dialog":
                    return new Dialog(){HasTitle=false};
                case "window":
                    return new Dialog(){HasTitle=true};
                case "frame":
                    return new Frame();
                case "terminal":
                    return new Terminal();
                case "keycapturer":
                    return new KeyCapturer();
                case "dropbox":
                    return new Dropbox();
                case "griddropbox":
                    return new GridDropbox();
                case "scroller":
                    return new Scroller<Widget>();
                case "listbox":
                    return new Listbox<Widget>();
                case "option":
                    return new OptionPicker();
                case "hidden":
                    return new Hidden();
            }