Guiliani  Version 2.5 revision 6773 (build 33)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Application set-up

Overview

Setting up a Guiliani application requires the following steps:

  • Deriving a class from CGUI.
  • Implementing an entry point (i.e. main function).
  • Choosing appropriate wrapper classes for the target platform
  • Building the actual GUI.

CGUI implementation

CGUI contains Guiliani's main loop, among other things. A class is derived from it which will later be instantiated in the main function.

It is important to keep this derived class platform-independent to ease later porting efforts.

Implementation details

When deriving a class from CGUI, its constructor must take at least the GUI dimensions and an object handle as its arguments and call the according base class constructor. The following example shows the constructor of a class CMyGUI that is derived from CGUI:

CMyGUI::CMyGUI(
eC_Value x, eC_Value y, // upper left corner
eC_Value width, eC_Value height, // size
:
CGUI(x, y, width, height, eID)
{
Init();
}

The method Init here is used for encapsulating further initialization code, like loading a localization file (see Creating the GUI). Also, if streaming is used, this is where stream readers or writers should be instantiated. For more information, see Streaming.

Likewise, a method DeInit should be implemented to perform clean-up (like deleting singleton instances that were created by Init).

Main function

The main function should be the only platform-dependent part in a Guiliani application. Typically, there will be a file containing the main function for each platform the application is ported to.

General initializations

The first thing that should be done by a Guiliani main function is registering one or more trace outputs (like stdout or debugger outputs). This can be done by using the macros defined in GUITrace.h or by implementing own observers and adding them to CGUITrace (see CGUISubject::AddObserver). This ensures that any errors reported by the framework can be seen.

Platform specific initializations

Each platform may require more or less specific initializations. The amount of work that has to be done here also depends on the graphics wrapper implementation. The SDL graphics wrapper for instance does not require any preparations before it can be instantiated; it opens its window itself. The Win32 graphics wrapper, on the other hand, needs a window handle (HWND) and therefore a fully initialized window.

Graphics and font wrapper

In order to display anything on a screen, a graphics wrapper has to be instantiated. Since most controls also display text, a font wrapper is required as well. This is a platform-specific choice and has to be done before the first Guiliani GUI object can be created. Use the CreateInstance methods of the chosen wrappers to initialize them.

Example scenario

The following is a typical use-case where an EGL-graphics wrapper, a FreeType font wrapper, image decoders for BMPs and PNGs and an Input wrapper for Windows Events are used within a Windows application.

void ConstructResourceClasses(HWND hWnd)
{
try
{
// Choose a graphics wrapper
CGfxWrapEGL::CreateInstance((NativeWindowType)hWnd, (NativeDisplayType) GetDC(hWnd));
// Choose a font wrapper
// Add image decoders. If this gives you a compiler error, add the decoders to the project.
GETIMGLOADER.AddDecoder(new CGUIImageDecoderBMP());
GETIMGLOADER.AddDecoder(new CGUIImageDecoderPNG());
// Choose an input wrapper
// Tell the resource file handler where the GUI resources (images etc.) are located
GETRESHANDLER.SetResourcePathPrefix("../GUIResources/");
}
catch(...)
{
GUILOG_EXCEPTION( CGUIException(), "Failed to initialize wrapper classes.\n");
}
}

Running the GUI

After everything is set up and a graphics wrapper is instantiated, the previously derived class can be instantiated and its Run method can be called:

CMyGUI* pMyGui = new CMyGUI(
eC_FromInt(0),
eC_FromInt(0),
eC_FromInt(640),
eC_FromInt(480),
OBJ_GUI);
pMyGui->Run();
delete pMyGui;

This creates a GUI object with VGA dimensions and calls its Run method. This runs the main loop and returns only after the GUI has been shut down (see CGUI::Quit). The GUI object is deleted afterwards.

Shutting down

After the GUI returns from its Run method, everything that has been allocated in the set-up process should be cleaned sup before the main function exits.

This is a typical shutdown code, matching the instantiated wrappers from the example above.

void DestructResourceClasses()
{
// delete previously created wrapper classes
CGfxWrapEGL::DeleteInstance();
}

Creating the GUI

As mentioned before, any platform-independent initializations should be done in the class derived from CGUI (called MyGUI from now on). The constructor of MyGUI does things like loading a localization file (see CGUILocalisationHandler::LoadLocalisationFile) and registering fonts (see CGUIResourceManager::RegisterFontResource).

The most important steps, however, are

  • adding a composite object that contains the actual GUI and
  • invalidating the whole screen.

Adding the main GUI object

For better modularity, it is not recommended to add individual child controls directly to MyGUI. Instead, a separate class should be derived from CGUICompositeObject which contains the single buttons, input fields and other GUI elements. An instance of this class should be the only child of MyGUI. It can easily be removed and replaced by another composite object. Using this approach enables modeling different application screens or dialogs.

Invalidating the screen

After MyGUI has done all its initializations, the graphics wrapper must be instructed to draw the whole screen at least once. This can be done with the following line of code:

GETGFX.InvalidateRect(0, 0,
eC_FromInt(GETGFX.GetPhysicalScreenWidth()),
eC_FromInt(GETGFX.GetPhysicalScreenHeight()) );

Afterwards, the controls take care of invalidating only the areas that need redrawing.

Important: Invalidating the whole GUI (for instance by using a line like above or by invalidating the main composite object) should be avoided at all cost since it leads to DoDraw calls on all objects. Drawing is among the most expensive operations in the framework.