GUI Window Tutorial

Revision as of 15:48, 4 November 2011 by Alex (Talk | contribs)
Jump to: navigation, search


He intermediate.png


This is a step by step tutorial on how to create a simple GUIControl - a popup window which says, "Hello World."

It is important to note that this is procedural (that is, script-based) way of creating a GUIControl. This system uses a script to set the various attributes of the control, by using pre-created prototypes, creating GUIControl nodes based on those prototypes, and then modifying the nodes' various attributes individually. Another way is to use the GUI Editor, which allows for a temporary Control to be created and/or configured in a visual manner.


Before going any further, you should already have worked through at least the following tutorials:

You should have HeroBlade open, and the script editor. Either create a new client-side script, or use one of your existing client-side work scripts where you can add the necessary function. Whichever script name you use, should be inserted in the place of <script-name> below.

This tutorial has been written in a "cookbook" fashion so that it is not necessary to understand each GUIControl attribute. If you want though, there is a detailed line by line explanation of the modifications, farther down on this page.

To learn more about the details of other GUIControl attributes which were not modified in this tutorial, please see the comprehensive list of Base Fields on the GUIControls page. The documentation there also includes a wealth of other information about the different Controls, including the default and potential values for each of their fields.


Tutorial Overview

The following function will create a new GUIControl called myTestWindow, based on the prototype window_vscrollable. This new TestWindow will already have several necessary subcontrols, such as the ones necessary for creating a window titlebar, and a work (client) area within the body of the window.

The code in this function modifies the attributes of the subcontrols directly. It also creates an additional new GUIControl, called myTestDataLabel, based on the label prototype. The new label will have text added to it, and then be connected to the client area of myTestWindow for display.

When the function is run, the window and associated text will appear in the center of the viewport.

Create the function

A detailed line-by-line analysis of this code is available further down on this page. For now though, it can suffice to simply type in the following, or copy/paste this function into your client-side work script:

function MakeTestWindow()
                                         // This creates a window GUIControl from a prototype
  myTestWindow as NodeRef of Class GUIControl = createNodeFromPrototype("_window") = true              // Make the new window renderable in the screen = "A Test Window"
  myTestWindow.size.x = 300
  myTestWindow.size.y = 100
                                        // Configure the window's position in the viewport
  screen as Vector3 = getViewPortSize()
  myTestWindow.position.x = (screen.x - myTestWindow.size.x) / 2
  myTestWindow.position.y = (screen.y - myTestWindow.size.y) / 2
                                        // Find the titlebar text control on the window, and modify it
  myTitle as NodeRef of Class GUILabel = findGUIControlByName(myTestWindow, "titlebar.title")
  myTitle.text = "The Amazing Test Window"  = true
                                        // Create the Label where the main window text will appear
  myTestDataLabel as NodeRef of Class GUILabel = createNodeFromPrototype("_label") = "My Test Window's Data Label"
  myTestDataLabel.text = "Hello World!"
  myTestDataLabel.autoSetHeight = true
                                        // Find the clientarea on the window control, and connect
                                        //   the new text label control to it.
  clientArea as NodeRef of Class GUIControl = myTestWindow.getClientarea()
  add back myTestDataLabel to clientArea.children
  myTestDataLabel.dockMode = TOP = true         // This makes the new label renderable in the screen

Detailed Explanations of the Function's Code

The function takes the following steps:

Detailed explanation of these steps is below:

Create the GUIControl Window Node

myTestWindow as NodeRef of Class GUIControl = createNodeFromPrototype("_window")

There are many pre-made prototypes that exist for creating a window. For example: A basic window, a scrollable window, a resizeable window, etc. For this example, the _window prototype was used. This prototype is for a window that can be scrolled vertically, and also resized. If it was desired to use a prototype which was scrollable but not resizeable, then a different prototype could have been used.

Each GUI control prototype has its own XML file. The available prototypes for GUI controls are listed in the Organizer under the GUIXML tab.

Make the GUIControl Renderable = true

When the build flag is set to ~TRUE, that tells the Hero's Journey engine that this Control is okay to render on the screen. If the Control were still in a half-finished state, this flag would probably be kept at ~FALSE so that the Control wouldn't be rendered yet.

Name it = "Test Window"

All GUIControls must have a name, so that they can be referenced by the GUI Engine and by HSL. The name can include spaces and different kinds of capitalization. Note that a "name" is usually different from the "text" of a control. In most cases, a Control's name is not seen by the user -- it's strictly for internal use. To add something that is actually visible on the screen, this usually involves modifying a "text" field.

Give it a size

  myTestWindow.size.x = 300
  myTestWindow.size.y = 100

This sets the new Control's width (the x value) and height (the y value), in pixels.

Position the Control on the screen

  screen as Vector3 = getViewPortSize()
  myTestWindow.position.x = (screen.x - myTestWindow.size.x) / 2
  myTestWindow.position.y = (screen.y - myTestWindow.size.y) / 2

If a position is not specified, the Control will be positioned at 0,0, which means that its upper left corner will be at the far upper left of the viewport (the game screen).

To center the Control, first determine the current size of the Viewport, with getViewPortSize(). This returns a value in vector3 format, where the x and y values are the current width and height of the Viewport. A simple calculation between the size of the Control and the size of the Viewport (divided by two) provides the perfect location at which to position the Control. However, the Control could also be positioned anywhere else in the Viewport as well. For example:

myTestWindow.position.x = 300
myTestWindow.position.y = 300

Note that the position will change automatically if the user decides to drag the window around or resize it. The position fields in this case are merely to determine the Control's starting location.

Put text in the title label

This particular prototype that has been used, window_vscrollable, already has several necessary child controls, some of which have their own children. For example, the window has a child control titlebar, which has a child titlebar_body, which has a child Control label text, which has a subfield "text", which is what actually displays as the name of the titlebar to the user.

In order for the script to change the label, it's necessary to know the node reference for that Control. A handy function for this is findGUIControlByName(), which takes a parent Control to search through, and the name of the Control that it's looking for, and then returns a Node reference if it is found.

myTitle as noderef of class GUILabel = findGUIControlByName(myTestWindow, "titlebar.title")

Once the noderef has been found, the Control can be modified:

myTitle.text = "The Test Window"

The text can also be centered:  = true

This will attempt to center the text within the confines of the Label Control.

Another thing to note about this GUIControl, is that we're using a specialized form of Control, called GUILabel. The GUILabel Class inherits most of its fields from the base GUIControl, but also has a couple specialized fields, such as "text". There are about a dozen different Classes that inherit from GUIControl, such as GUIRadioButton, GUITextInputBox, GUINumericUpDownButton, and so forth. For a complete list of the classes, plus details on the fields of each of them, please see the section on GUIControls.

Create a label to hold your content

In this step, the function creates a new GUILabel Control, based on the _label prototype.

  myTestDataLabel as NodeRef of Class GUILabel = createNodeFromPrototype("_label") = "My Test Window's First Data Label"

Put some data in the new label

myTestDataLabel.text = "Hello World!"

Set the size of the label

It's possible to set the size to a specific value, or it can be automatically determined. In the case of this tutorial, the size is being set automatically:

myTestDataLabel.autoSetHeight = true

This automatically make the size of the label large enough to accomodate however much text is in it.

Note: autoSetHeight does not modify the size of the font -- only the size of the Control that contains the font. Though it's possible to make a larger font by scaling a Control to a larger size (which scales the font along with it), this does not look very clean. As of this writing, there is not any way to elegantly change font size or type.

Dock it

A child control must be located somewhere on the parent control. It can be positioned by specifying its exact x,y coordinates, or it can be "docked" to the parent, so it will stretch and position itself automatically. For example a child control that is "Dock Left" will match its left edge to the left edge of the parent, and stretch itself vertically so that it is the same vertical size of the parent.

Note that in a subsequent step we are going to set this label's parent to be the clientarea, so in the case of this window, you can read "parent" to mean "clientarea" in the descriptions above.

There are six dock modes:

Since our child control window is vertically scrollable, it works best to set its dockmode to TOP. We know how wide it is, but we don't want to have to know how high the window needs to be. That's why we made it scrollable.

The FILL option would not work as well. True, it will take up as much space as it needs, but it would not scroll because its size would be defined by the visible size of the clientarea. We want to have access to areas beyond the visible boundaries of the clientarea if we need it.

Note: At some point in the future, this may be handled differently, and a Dock Mode of ~FILL might allow a child control to still progress beyond the bottom of the parent control if it needs to.

myTestDataLabel.dockMode = TOP

Find the Client Area Control

This is a control that's already a child of myTestWindow. To manipulate it though, we need a node reference to it. So we need to "find" it on the parent, using the getClientArea() method (optionally we could have used FindGUIControlByName() function).

clientArea as NodeRef of Class GUIControl = myTestWindow.getClientArea()

This control defines the panel that will hold the things that you want to display. It's what's left of the window after taking account of the resize bars, title bars, etc. The clientarea fills up all the spaces between all these things.

Add the Label to the List of Client Area Children

add back myTestDataLabel to clientArea.children

This adds the new Label to the end of the list of children of ~clientArea. If other labels are created, they can be added in behind this one.

Build it

This step was not necessary for the myTitle label, as it was already a direct child of the window. In other words, we "found" it, we didn't create it. Since myTestDataLabel was created by the script though, and then linked in, its Build flag still needs to be toggled true: = true

Additional information

Personal tools