GUI API OverviewAbstract: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 NOT been updated in this release. Beware that some issues might not be correct compared to the ClanLib version you have installed. Implementing a GUI with ClanLibThanks 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. GUI's are defined in special GUI definition script files, that 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. 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 hopefully be written soon. 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 the short description of signals and slots below. A primer: Signals and SlotsComponents 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'. One solution to this problem could be for the game to poll the inputbox for each update. It would obviously be annoying and time-consuming to do this for each component, so most GUI systems use a callback system of some sort. ClanLib is no different than most GUI systems in that respect. In ClanLib, each component has a set of 'signals' that are 'emitted' when an event occurs. What does this mean? Well, the user can 'connect' a 'slot' into a 'signal' and thereby have the emitted 'signal' go down each 'slot' plugged into it. Confused? Don't be - when a game needs to get informed when the player presses 'enter' in the inputbox for instance, the game can associate a function with a slot, and 'connect' that slot to the signal of the inputbox in question. class Inputbox { public: CL_Signal_v1<const std::string &> &sig_changed(); }; class MyClass { void init(); void on_changed(const std::string &new_value); CL_Inputbox *inputbox; CL_Slot slot; } void MyClass::init() { inputbox = new CL_InputBox(CL_Rect(140, 10, 280, 0), "Input here", parent); CL_Slot 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. } This code probably looks a bit strange, and require some explanation. Signal and slot systems are generelt built using templates, as this makes it possible to benefit fully from the C++ type system. The Inputbox class has a signal. The template-type of the signal class is called CL_Signal_v1. The _v1 means void, 1 parameter. If we wanted a type with 2 parameters, we would create a signal of type CL_Signal_v2. When a signal is created, it can be triggered by invoking the signal as a function (the object overloads the () operator). Since our sig_changed signal expects one argument, we should pass a 'const std::string &' to the function, like this; input_box->sig_changed()("hello, world"). This will in turn invoke the function associated with all slots connected to the signal. Slot objects maintains connection state to the signals to which they are connected. When a slot object is destroyed, the signals to which it was connected will be automatically informed, and subsequently disconnected. However, this also means that it's important to be aware that you have to store the slots somewhere (even though they slots themselves are never used actively). GUI definition files (.gui)As mentioned, GUI's in ClanLib are initially 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 aquainted with the GUI definition format. GUI files does not have sections. Instead, components that can contain child-components (hereafter called containers), 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. Basic component typesThese basic component types exist as of this date:
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. *** Overview not finished yet - look at the source for further details regarding component initialization values :-) ***GUI classesApart from the GUI components described previously, there also exists some classes that constitute the backbone of the ClanLib GUI. These classes are the GUI-manager, the component-manager and the style-manager.
The component-managerThe 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. The GUI-managerThe 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). |