UI toolkit implementation

Jump to navigation Jump to search

This document is about the implementation of the code of the toolkit used to manage user interface.

Draft: Need to rewrite most of the things


Script based

See also UFO-Scripts/ui/*.ufo‎, for script language.

Most of the GUI is build with scripts. The script describes the construction of the GUI, the style, and also some interaction.

  • Construction is based on node tree, node properties, events, and actions. We can create tree of nodes one by one. There is some way to reuse script code, with concept of inherited windows, and with concept of component (a way to clone a subtree of nodes). See UFO-Scripts/ui/*.ufo‎ for more information about the language.
  • A big part of the style of the GUI is on the node properties (image, color, font...) but a little part can be into node behaviour C code. Components can be a way to define and reuse a style.
  • Common interactions from the user to the game come from the GUI script. Global idea is to have an abstraction between the client code (the C code) and the GUI script, what why the script also provide callback functions for the client (confunc) to update itself his status to the GUI. See more information in #Client and script interaction.


A view of the architecture

We can globaly split the software architecture of the game GUI into three.

  • client code: It contain data of the party
  • GUI code
    • core: It handles user-computer interactions across the GUI code.
    • node behaviour: It provides common interactions, but also a way to customize it. Common interactions also can used to reduce complexity of script. Some are "client node", not generic, very specific to this game and his data. For more information see #Node behaviour.
    • data architecture: architecture generated by the interpretation of scripts
  • core code: it provide access to the resource, and in the case the GUI, provide some way for client-GUI interaction. For more information see #Client and script interaction.

Data architecture

  • windows and node trees
  • fonts
  • icons
  • UI bindings

Node behaviour

Node behaviour are introduced to reduce complexity of the GUI code decoupling the core code and stereotyped interactions (node). As a result, it provide a way to custom the GUI by creating new nodes.

Each nodes from script are typed (pic, spinner, confunc...) We provide for each type a common behaviour. We can see it as an OO, where each node instance take behaviour from his class.

For the moment node behaviour provide some element of customisation.

Inner data per node:

  • common data: Data come from "abstractnode". Mostely generic properties like position and size, and events like mouse click. But it also provide properties often need by others behaviours (like text color, font...)
  • extra data: Data providing only for instance of a behaviour. For the moment its not very easy to add new extra data because there are a big coupling between the "common data" and each "extra data"; but its the only way.

Script interactions with:

  • properties: Named properties provided access to inner of the node (read, write) allow script based get/set
  • actions: Properties defined from script with a list of actions. The node execute it on an event. It often executed by the core GUI itself, if the node dont catch some event.

User-GUI interaction with:

  • catch mouse input: click, down, up, move...
  • catch draw: allow do draw what we want
  • catch core event: before and after script initialisation...
  • ... and we can add more anchor like that

Client and script interaction

The core code of UFOAI provides some way to exchange informations between GUI and client code.

  • cvar: It provide a named data (float or string). This global elements can be read and write by both client and script. Also some node properties can be linked to a cvar. String size can't be bigger than 255 characters.
  • command: Named entry, often to a client function. A command is often registered by the client to interact with the game data. Some are available to override lacks of the script.
  • confunc: A confunc is a function script based embedded into a node and available as commands like all other script commands.
  • shared data: A special way are provided to access to some data like linked list, or big text buffer (more than 255 characters). It is available as numbered slots. It should be nice to clean up this to use more generic way. See m_data.c

Client can also direct access the nodes by name, without any call of confunc, but it is not encouraged to promote more abstraction. If we call a confunc, we can update this confunc without need of recompiling the game.

Mouse events

Three examples.

The mouse can send events when we use buttons, but also when we hover a nodes. When a mouse enter or leave over a node, we can execute a script event; when we move the mouse over, we can't.

It's helpfull to understand a bit how work the core GUI to script well. First of all, the architecture only allow to hover one node at a time. When we use the mouse button, we only interacte with this one. The order of the node description into the script is important, because it say which node is over. Nodes are ordered like stack, on this example N1 is over N2. If the mouse is inner the bounded box of N1 it will not hover N2 (and we can't click on N2).

menu W
    button N2 { ... }
    button N1 { ... }

Some examples:

  • The first example show a node out of a window (menu). The core check the bounded box of a window before testing all nodes. If a node is out of his parent menu, we can't hit it with the mouse. In this example, the mouse dont houver the blue node (but we can hover the part of the node inner the window).
  • Second example help to understand when we call in and out events. The mouse start from the node 1 to move over the node 2. We first call the out event of the node 1, and than we call the in event of the node 2.
  • On the example 3, when we hide the hovered node, we also send the same event. Without moving the mouse, if we hide the node 1, the node 1 will receive the out event and then the node 2 will receive the in event.
    There are limitation, if a node became visible, or is translated under the mouse, we currently can't check the right hovered node without moving the mouse.

Node tangibility

Some examples.

We can force to click through a node. We can take the example of a menu w2 under a menu w1, and add a node into the the first menu.

menu w2 { }
menu w1 { node n1 { } }

Two mechanism are provided, both are apply to a node, not to his child.

We can define many expluded rectangle to a node. Rectangle are set relative to the node. The mouse over a an excluded rect will click through the node, not not through the node's child. The example show a mouse who will click on a child. See "How to script menu" for an example.
An easy way is to exclude a full node. Instead of using an excluded rectangle with the size of the node (create problem if we resize the node), we can set the ghost property. It will work like excuded rectangle; the example 2 show every where we can click on w1, we will click through, else if we are over a child.


How to add a node behaviour

The more easy to start is to copy-paste an existing node.

  • Take a simple (header should have only one function MN_RegisterNODENAMENode) node behaviour into /client/menu/node/ and copy it with a new name.
  • Change the name of the register function. It should be MN_RegisterNODENAMENode.
  • Change the name of the behaviour name behaviour->name = "NODENAME" (without uppercase or space)
  • Update /client/menu/m_nodes.c
    • Add your new include on the top of the file
    • Add your register function at the right position on registerFunctions. All functions must be sorted by node behaviour name.

Now you can define node with your node behaviour into the script.