QuickGUI Basics        

Introduction

This tutorial explains how to use QuickGUI to create a User Interface, and listen to events. It is already assumed QuickGUI is integrated into your application. If you have not completed the Integration tutorial, please finish that first.
QuickGUI Integration Tutorial

Fonts

QuickGUI does not resize Fonts when used in your application. Fonts are provided to QuickGUI via the FontManager, and consist of an XML and image file. The XML file describes where font glyphs are in the image file, as well as each glyphs dimensions, and offsets, used when writing glyphs one after each other. By default I use FontStudio to create my Fonts. Using FontStudio it is easy to create outlined and shadowed Fonts.

FontManager

The following code is taken from the QuickGUIOgreDemo project, and registers Fonts with QuickGUI:

// Register all fonts we plan on using
QuickGUI::FontManager* fontMgr = QuickGUI::FontManager::getSingletonPtr();
fontMgr->createFont("ControlFreak.14",mGUIResourceManager->getImage("ControlFreak.14.png"),10,prefix+"/resources/fonts/ControlFreak.14.xml");
fontMgr->createFont("Cuckoo.14",mGUIResourceManager->getImage("Cuckoo.14.png"),10,prefix+"/resources/fonts/Cuckoo.14.xml");


Explanation of parameters:

  1. The name of the Font, must be unique. I generally include the name and the size of the Font.
  2. The image file on disk that visually describes how the glyphs look.
  3. The baseline, or amount of pixels from the top of the glyphs to the imaginary line that all glyphs sit on when drawn. All glyphs in a particular Font will have the same height. The baseline is useful for having multiple Fonts on the same line. For example, imagine various Fonts of various sizes next to each other, they will need to be aligned vertically to appear on the same line.
  4. The xml file describing the dimensions and offsets of each glyph in the Font. Imagine a pen drawng the glyphs one by one, from left to right. The offsets of each glyph tell us how far to move the pen before drawing the glyph, and after drawing the glyph, so that the text appears as you've come to expect. Each glyph has its own offsets, and offsets can be negative, positive, or zero.

Skins

This section will only cover the very basics of Skins. Skins are very powerful, and will be covered more in depth in another tutorial.

Skins describe the images that visually represent an object. A typical Skin will consist of SkinElement and SkinReference objects. Skins are registered with QuickGUI using the SkinManager.

SkinManager

The following code is taken from the QuickGUIOgreDemo, and shows how a Skin is registered:

// Register all Skins we plan on using
QuickGUI::SkinManager* skinMgr = QuickGUI::SkinManager::getSingletonPtr();
skinMgr->loadSkins(prefix+"/resources/skins/Button.skin");

Skins are written in XML format, and will look like this:

<Skin name="Button">
    <Type type="string" value="Button" />
    <TimelineLength type="float" value="0" />
    <SkinElement name="default">
        <TileBackground type="bool" value="false" />
        <TileBorders type="bool" value="false" />
        <Color type="ColorValue" value="1 1 1 1" />
        <Rotation type="int" value="0" />
        <Image type="string" value="qgui.button.png" />
        <BottomBorderThickness type="unsigned int" value="3" />
        <LeftBorderThickness type="unsigned int" value="3" />
        <RightBorderThickness type="unsigned int" value="3" />
        <TopBorderThickness type="unsigned int" value="3" />
    </SkinElement>
    <SkinElement name="over">
        <TileBackground type="bool" value="false" />
        <TileBorders type="bool" value="false" />
        <Color type="ColorValue" value="1 1 1 1" />
        <Rotation type="int" value="0" />
        <Image type="string" value="qgui.button.over.png" />
        <BottomBorderThickness type="unsigned int" value="3" />
        <LeftBorderThickness type="unsigned int" value="3" />
        <RightBorderThickness type="unsigned int" value="3" />
        <TopBorderThickness type="unsigned int" value="3" />
    </SkinElement>
    <SkinElement name="down">
        <TileBackground type="bool" value="false" />
        <TileBorders type="bool" value="false" />
        <Color type="ColorValue" value="1 1 1 1" />
        <Rotation type="int" value="0" />
        <Image type="string" value="qgui.button.down.png" />
        <BottomBorderThickness type="unsigned int" value="3" />
        <LeftBorderThickness type="unsigned int" value="3" />
        <RightBorderThickness type="unsigned int" value="3" />
        <TopBorderThickness type="unsigned int" value="3" />
    </SkinElement>
</Skin>


Each class is represented by a tag, using the format <Skin name="mySkinName">
Properties are represented using the format <PropertyName type="data type" value="value" />

You can see from the Button Skin provided, the Images used are "qgui.button.png", "qgui.button.down.png", and "qgui.button.over.png". If your resource manager does not have access to these images, the Skin will throw an exception when trying to be drawn.

Creating a Window

Windows are created from the Interface class. All UI will appear in the form of Windows. Windows represent textures that are applied to RenderTargets, which can be 2D (Overlay) or 3D (UIPanel). Creating a Window is very simple:

QuickGUI::Window* newWin = mInterface->createWindow("Test");

By default this Window will not have a Skin, and will not be visible in your application, even if attached to a RenderTarget. Here is how you would add a Skin to the Window:

newWin->setSkin(QuickGUI::SkinManager::getSingletonPtr()->getSkin("Window"));

NOTE: It is assumed that a Skin of type Window has already been registered with the SkinManager. If this has not been done, an exception will be thrown!
NOTE: There is nothing wrong with a Widget not having a Skin. For example, if you wanted a 3D ProgressBar or Label, you might want to create a Window with no Skin, so that only its child Widgets will be visible.

Now that we've created a Window, how do we add child Widgets?

Creating a Button

Aside from the Window, most Widgets can be created simply by supplying a name:

QuickGUI::Button* newButton = new QuickGUI::Button("MyButton");

Once you've created the Button, it can be added to the Window like this:

newWin->addChild(newButton);

Interacting with the GUI

Now that we're able to create a Button, how can we find out when it is clicked?

Message

QuickGUI relies heavily on the use of the MessageManager, which broadcasts Messages to MessageHandlers that are subscribed. Messages consist of 3 things:

  1. The type of the Message. A default list of Messages are supported, however this is just a string, and you can create any Message type you like.
  2. The source of the Message. This is the string identifier representing the source of the Message. This will often be a Widget's name, or an Interface's name, or "Core", for example.
  3. The args associated with the Message. There are many types of args: WidgetEventArgs, MouseEventArgs, TimeEventArgs, etc. They all derive from EventArgs, and you will have to dynamically cast them to extract any data you need.


NOTE: Not only can you receive Messages, but you can use the MessageManager to queue and broadcast Messages. You can modify Messages and re-send them out. Or create custom ones. The messaging system is very flexble!

MessageHandler

A MessageHandler is any class that inherits from QuickGUI::MessageHandler. This class requires the API void handleMessage(Message m) to be implemented.

class _QuickGUIExport MessageHandler
{
public:

	virtual ~MessageHandler();

	virtual void handleMessage(Message m) = 0;

protected:
};

MessageManager

Below are the main APIs of the MessageManager class:

/**
* Subscribes a MessageHandler instance to receive messages of a certain type.
*/
void subscribeToMessage(const std::string& messageType, MessageHandler* instance);
/**
* Subscribes a MessageHandler instance to receive messages of a certain type, from a specific source.
*/
void subscribeToMessage(const std::string& messageType, const std::string& source, MessageHandler* instance);

/**
* Unsubscribes a MessageHandler instance, preventing it from receiving messages of certain types.
*/
void unsubscribeFromMessage(const std::string& messageType, MessageHandler* instance);
/**
* Unsubscribes a MessageHandler instance, preventing it from receiving messages of certain types from a specific source.
*/
void unsubscribeFromMessage(const std::string& messageType, const std::string& source, MessageHandler* instance);


An important thing to note here is that you can subscribe generally to a Message of a given type, or specifically to a Message of a given type from a given source. For example, I could subscribe to a particular Button being clicked, or subscribe to any click. This is useful in a variety of scenarios.

The following code shows how to subscribe to MouseButtonUp Messages, when sent from our Button created earlier:

QuickGUI::MessageManager::getSingletonPtr()->subscribeToMessage(QuickGUI::DefaultMessageType::MouseButtonUp,newButton->getName(),myMessageHandler);


myMessageHandler refers to an instance of a class that inherits from QuickGUI::MessageHandler. This instance will receive a Message whenever my Button instance (newButton) broadcasts a Message of type MouseButtonUp. The code to handle the Message would look something like this:

void MyClass::handleMessage(QuickGUI::Message m)
{
    if(m.messageType == QuickGUI::DefaultMessageType::MouseButtonUp)
    {
        ; // do something useful
    }
}

Conclusion

We covered Fonts, Skins, creation of Widgets, and message handling! If you are still having problems in any of these areas, please post on the QuickGUI Forums.