ClanLib logo

GUI API Overview

Abstract:

A new API as of ClanLib 0.5 is the ClanLib GUI (Graphics User Interface). This document describes the ClanLib GUI API as of April 2001. The GUI is still subject to change, but the basic structure should be solid, and people are encouraged to try it out, as it provides an easy flexible and customizable way to do GUIs in games.

Note: This overview has only been partially updated in this release. Beware that some issues might not be correct compared to the ClanLib version you have installed.

Implementing a GUI with ClanLib

Thanks to the new GUI API in ClanLib, having a GUI in your game no longer has to be an ardous boring task. ClanLib GUI comes complete with implementations of most standard widgets/components, such as inputboxes, scrollbars, comboboxes, menus, listboxes, checkboxes and buttons.

At the core of the ClanLib GUI API, lies the routing of GUI input to game code. ClanLib uses a signals/slot system for this. If you are familiar with QT, you should also be familiar with signals and slots, but if you're in doubt, you can read our signals and slots overview.

The ClanLib GUI has a default look, eg. the way each component is displayed, but the logical functionality of each implemented base-component is entirely isolated from the displaying, so it's easy to create a customized GUI "style". Styles are managed through a style-manager that makes it easy to extend the default-style with new ones. This makes it possible for people to create a new GUI style, and release it for easy use by other ClanLib-users. An overview on how to create custom style will be written sometime soon.

GUIs can be defined in special GUI definition script files, which provide a flexible and powerful way to describe the layout of a GUI. Components are layered in a hierachical fashion where certain component types (such as a frame) can have child-components. Each component has a set of parameters (mandatory or optional) that describe the component, and through a naming-scheme, these components can be modified and hooked to callback functions at run-time.

Basic component types

These basic component types exist as of this date:

  • Button
  • CheckBox
  • Combobox
  • Frame
  • Image
  • Inputbox
  • Label
  • ListBox
  • MenuBar
  • PopupMenu
  • ProgressBar
  • ScrollBar
  • TreeView
  • Window

These basic dialogs exist as of this date:

  • FileDialog
  • MessageBox

These helper classes exist as of this date:

  • MoveHandler
  • ResizeHandler
  • LayoutManager

GUI classes

Apart from the GUI components mentioned above, there also exists some classes that constitute the backbone of the ClanLib GUI. These classes are the GUI-manager, the style-manager and the component-manager.

  • The GUI-manager is the primary runtime class. It handles input routing to all GUI components, and is also responsible for drawing the GUI.
  • The style-manager is responsible for actually instantiating components, and make sure they use the the correct style/theme. The style-manager is also the component that makes it possible to add new component-types to the GUI system.
  • The component-manager is primarily used at "walk-time", eg. when the game is initialized. It's responsible for loading the GUI definition file, and for mapping component string handles (component names) to instantiated component pointers. Its optional, unless GUI definition files are used.

The GUI-manager

The GUI-manager is unique connected to a component manager for initialization purposes. There can be several GUI-managers instantiated, but normally only one manager will be enabled at any given time. A GUI-manager can be created by calling the CL_GUIManager::create() functions, or by instantiating it as a normal object. A GUI-manager can optionally get passed reference to a component manager, in which case all components in that component manager, will be added to the GUI-manager. The GUI-manager attaches itself to the ClanLib input system, and routes input to components. Input-routing is dependent on focus. The GUI-manager keeps track of which components have focus, and also mask input events according to relevance (for instance, mouse event's are only sent to components beneath the mouse cursor). The GUI-manager also implements mouse-capture support, move-component support and a show-sequence that is dependent on focus (a component that is clicked on is moved to the front).

The style-manager

The component-manager

The component-manager is unique for each .gui file loaded. Create a component-manager by invoking the CL_ComponentManager::create() function, passing along the name of the .gui file to be opened, or the handle to a raw-provider in a resourcemanager, containing the file. The .gui file is immediately opened and parsed, and any errors are thrown as CL_Error exceptions. When succesfully instantiated, get component-managers get_component method can be used to retrieve a pointer to a component. At the moment all component names must be unique throughout a .gui file, but it's very likely that this will be changed in the future, so that component names are qualified with the names of all parents (like 'main_frame/player_name'). Since the component manager only know's that it's a component, you'll have to manually cast the component to the specific component type (for instance use dynamic_cast<>). When a specific component type pointer has been obtained, you can connect slots to signals, binding functions to component instances.

Signals and Slots

Components have certain events that need to be acted upon by the game code. For instance an inputbox allowing a player to enter his/her name must be acted upon when the player presses 'enter'.

    // CL_InputBox is one of the common components provided with ClanGUI
    class CL_InputBox
    {
    public:
    	...
    	CL_Signal_v1<const std::string &> &sig_changed();
    	...
    };
    
    class MyClass
    {
    	MyClass(CL_Component *parent);
    	void on_changed(const std::string &new_value);
    	
    	CL_InputBox *inputbox;
    	CL_Slot slot;
    }
    
    MyClass::MyClass(CL_Component *parent)
    {
    	inputbox = new CL_InputBox(CL_Rect(140, 10, 280, 0), "Input here", parent);
    
    	slot = inputbox->sig_changed().connect(CL_CreateSlot(this, &MyClass::on_changed));
    }
    
    void MyClass::on_changed(const std::string &new_value)
    {
    	// Inputbox has been updated now, and the new text is in new_value.
    }
    

Note that the signals are a little bit different than the ones described in our signal and slots overview:

    	CL_Signal_v1<const std::string &> sig_changed;     // As described in the overview
    	CL_Signal_v1<const std::string &> &sig_changed();  // As used in ClanGUI
    
    	slot = inputbox->sig_changed.connect(CL_CreateSlot(this, &MyClass::on_changed));    // As described in the overview
    	slot = inputbox->sig_changed().connect(CL_CreateSlot(this, &MyClass::on_changed));  // As used in ClanGUI
    

As a ClanGUI user, you only have to remember the extra () after the signal - sig_changed(). If you create your own signals, you use the method described in the signals & slots overview.

GUI definition files (.gui)

As mentioned, GUIs in ClanLib can be described using a GUI definition file (recommended extension: .gui). The GUI definition format looks a lot like the ClanLib resource manager format, so if you're familiar with that, you should easily get acquainted with the GUI definition format. GUI files does not have sections. Instead, components that can contain child-components, simply function as "sections". This means that the hierachical structure of the GUI is directly readable from the GUI definition file. Let's look at a small example;

    frame main_frame
    {
    	x = 50;
    	y = 50;
    	width = 500;
    	height = 400;
    
    	inputbox player_name
    	{
    		x = 10;
    		y = 10;
    		width = 200;
    		value = "Player Name";
    	}
    
    	button start_game
    	{
    		x = 220;
    		y = 10;
    		width = 64;
    		height = 32;
    		text = "Start";
    	}
    }
    

A frame is a component that has no other purpose but to group components. It serves as a base "window" that can be used for dialog boxes etc. As this component is at the top of the component hierachy, the x, y position is directly mapped to initial screen coordinates. Each sub-somponent's x/y values are relative to the current position of their parent. This means that player_name will always be offset (10, 10) pixels from the upper-left corner of main_frame, and that both player_name and start_game will be dragged along if main_frame is moved. Width and height is not relative to any parents, and is measured in pixels also. However, all sub-components are clipped within the screen area of the parent.

Every component type recognize different sets of values in the .gui file. Some values are mandatory for a component (they have to exist in the .gui-file for the initialization to succeed), and some are optional, and have default values in case they don't exist. All component types require the 'x' and 'y' value, describing the position/offset of the component in question.



Questions or comments, write to the ClanLib mailing list.