Creating a custom GUI style - ClanLib Game SDK

#

## Creating a custom GUI style

ClanGUI contains a powerful mechanism to code custom themes, or styles as we call them in ClanGUI. Styles are very flexible, yet very easy to implement. This document will guide you through creation of a custom style.

### Styles overview

Each component in ClanGUI is clearly separated into logic and presentation. When creating a new style for a component (for example CL_Button), you create a new class which does the drawing of a component - the logic is still the same between different styles.

A CL_StyleManager is used to attach styles to each component available. So in order to create a new style you need two parts - a stylemanager, and then the style-classes for each component you want a new look for.

### When do I need to create my own GUI style?

The standard GUI style in ClanGUI very much resembles any desktop-gui used in applications. This doesn't always fit with games.

Some of the components in the default GUI style supports surfaces, like CL_Button. So, if you only wants some nice-looking buttons in your game which use predrawed pictures, you can still use the default style. But if you want a custom-looking inputbox or scrollbar, you need to create a custom style.

### Creating a stylemanager

Create a new file called stylemanager_coolstyle.h, and insert the following code into it:

    #ifndef header_stylemanager_coolstyle
    #define header_stylemanager_coolstyle

    #include <ClanLib/gui.h>
    #include <ClanLib/guistyleboring.h>

    class CL_StyleManager_CoolTheme : public CL_StyleManager_Boring
    {
    public:
    	CL_StyleManager_CoolTheme(CL_ResourceManager *resources);

    	// Connect component styles to component.
    	// The 'type' parameter indicates what type the component is.
    	virtual void connect_styles(const std::string &type, CL_Component *owner);
    };

    #endif

The new stylemanager (CoolTheme) inherits the CL_StyleManager_Boring. This means it will use the default style for any components you don't define a new style for. Note that it is not neccessary to inherit from CL_StyleManager_Boring - you could also inherit from CL_StyleManager, but if you then try to use a component for which you haven't created a style for, nothing will be drawn for the component. You could also use the CL_StyleManager_Silver for a slightly fancier default style.

The connect_styles() is the function which attaches the styles to the various components. Make sure you inherit this function.

Next, create a new file called stylemanager_coolstyle.cpp, and insert the following code into it:

    #include "stylemanager_coolstyle.h"
    #include "button_coolstyle.h"

    CL_StyleManager_CoolTheme::CL_StyleManager_CoolTheme(CL_ResourceManager *resources)
    : CL_StyleManager_Boring(resources)
    {
    }

    void CL_StyleManager_CoolTheme::connect_styles(const std::string &type, CL_Component *component)
    {
    	if (type == "button")
    	{
    		CL_Button *button = (CL_Button *)component;
    		button->set_style(new CL_Button_CoolTheme(button, this));
    	}
    	else
    	{
    		CL_StyleManager_Boring::connect_styles(type, component);
    	}
    }

A file called button_coolstyle.h is included here; we'll get back to that in the next section.

The connect_styles() function has two parameters - type and component.

The type parameter contains the name of the component to be styled - examples are label, button, frame, inputbox, scrollbar, radiobutton, listbox, window, etc. For each component you want to put a style on, make a new test for the type, and attach the style using the set_style() function in the component. Notice how its done through creating a new style class called CL_Button_CoolTheme.

Any components we don't attach, we just pass on to the default stylemanager. You can skip doing this if you have styled all the component you want to use, or don't want to use the default stylemanager at all.

### Creating a styled component

Create a new file called button_coolstyle.h, and insert the following code into it:

    #ifndef header_button_coolstyle
    #define header_button_coolstyle

    #include <ClanLib/gui.h>
    #include "stylemanager_coolstyle.h"

    class CL_Button_CoolTheme : public CL_ComponentStyle
    {
    public:
    	CL_Button_CoolTheme(CL_Button *button, CL_StyleManager_CoolTheme *style);

    private:
    	CL_Slot slot_paint;
    	void on_paint();

    	CL_Button *button;
    };

    #endif

Each component-style inherits from the class CL_ComponentStyle. The CL_Button object you get in the constructor is the logic class for the button. You can query it for states (is_down()) and size etc.

In this simple style, we only prepare to hook into the sig_paint signal - therefore the slot_paint and on_paint() code. Read on for more specifics on this.

Create a new file called button_coolstyle.cpp, and insert the following code into it:

    #include <ClanLib/display.h>
    #include "button_coolstyle.h"

    CL_Button_CoolTheme::CL_Button_CoolTheme(CL_Button *button, CL_StyleManager_CoolTheme *style)
    : CL_ComponentStyle(button)
    {
    	this->button = button;

    	slot_paint = button->sig_paint().connect(this, &CL_Button_CoolTheme::on_paint);
    }

    void CL_Button_CoolTheme::on_paint()
    {
    	int button_width = button->get_width();
    	int button_height = button->get_height();

    	CL_Display::fill_rect(CL_Rect(1, 1, button_width - 1, button_height - 1), CL_Color(70, 120, 255));

    	if(button->is_down())
    		CL_Display::draw_rect(CL_Rect(0, 0, button_width, button_height), CL_Color::white);
    	else
    		CL_Display::draw_rect(CL_Rect(0, 0, button_width, button_height), CL_Color::black);
    }

In the constructor we hook into the sig_paint signal of the CL_Button. This allows us to do custom drawing of the button.

The on_paint() function fetches the size of the button, and draws some simple rects, based on the button state - if its pressed down or not.

### Testing the new style

We have now created a very simple style, using only one themed component - a CL_Button. Lets try to test it, and see if it behaves as we expect it to do.

    #include <ClanLib/core.h>
    #include <ClanLib/gui.h>
    #include <ClanLib/gl.h>
    #include <ClanLib/application.h>
    #include <ClanLib/display.h>

    #include "stylemanager_coolstyle.h"

    class StyleTest : public CL_ClanApplication
    {
    public:
    	int main(int argc, char** argv)
    	{
    		try
    		{
    			CL_SetupCore setup_core;
    			CL_SetupGUI setup_gui;
    			CL_SetupDisplay setup_display;
    			CL_SetupGL setup_gl;

    			CL_DisplayWindow window("StyleTest", 640, 480);

    			CL_ResourceManager resources("gui.xml");
    			CL_StyleManager_CoolTheme style(&resources);
    			CL_GUIManager gui(&style);

    			CL_Button button(CL_Rect(10, 10, 100, 40), "", &gui);

    			while (!CL_Keyboard::get_keycode(CL_KEY_ESCAPE))
    			{
    				CL_Display::clear(CL_Color::grey);
    				gui.show();
    				CL_Display::flip();
    				CL_System::keep_alive(10);
    			}
    		}
    		catch (CL_Error e)
    		{
    			std::cout << e.message.c_str() << std::endl;
    		}

    		return 0;
    	}
    } app;

Remember to copy the Resources/GUIStyleBoring files into your project, so it has some fonts and resources to use. These can be replaced later with your own resources.

### Some example GUI styles

ClanGUIs default theme Silver | ClanGUIs default theme Boring | The game Supaplex
The game Race | The gameeditor in Race | The game APOC

Questions or comments, write to the ClanLib mailing list.