GUI Event Receiver

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

GUI Event Receiver

Post by sudi »

A simple gui Event receiver that allows binding gui elements to functions or methods:

Example (altered Example No. 5)

Code: Select all

/** Example 005 User Interface

This tutorial shows how to use the built in User Interface of
the Irrlicht Engine. It will give a brief overview and show
how to create and use windows, buttons, scroll bars, static
texts, and list boxes.

As always, we include the header files, and use the irrlicht
namespaces. We also store a pointer to the Irrlicht device,
a counter variable for changing the creation position of a window,
and a pointer to a listbox.
*/
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif

// Declare a structure to hold some context for the event receiver so that it
// has it available inside its OnEvent() method.
struct SAppContext
{
    IrrlichtDevice *device;
    s32				counter;
    IGUIListBox*	listbox;
};

#include "CGUIEventDispatcher.h"
class MyEvents
{
    public:
        MyEvents(SAppContext & context)
        {
            Context = context;
            env = Context.device->getGUIEnvironment();
        }

        bool onTransparentChanged(const irr::SEvent::SGUIEvent& event)
        {
            s32 pos = ((IGUIScrollBar*)event.Caller)->getPos();

            for (u32 i=0; i<EGDC_COUNT ; ++i)
            {
                SColor col = env->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
                col.setAlpha(pos);
                env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
            }
        }

        bool onNewWindow(const irr::SEvent::SGUIEvent& event)
        {
            Context.listbox->addItem(L"Window created");
            Context.counter += 30;
            if (Context.counter > 200)
                Context.counter = 0;

            IGUIWindow* window = env->addWindow(
                                     rect<s32>(100 + Context.counter, 100 + Context.counter, 300 + Context.counter, 200 + Context.counter),
                                     false, // modal?
                                     L"Test window");

            env->addStaticText(L"Please close me",
                               rect<s32>(35,35,140,50),
                               true, // border?
                               false, // wordwrap?
                               window);
            return true;
        }

        bool onQuit(const irr::SEvent::SGUIEvent& event)
        {
            Context.device->closeDevice();
        }

        bool onFileOpenButton(const irr::SEvent::SGUIEvent& event)
        {
            Context.listbox->addItem(L"File open");
            env->addFileOpenDialog(L"Please choose a file.");
            return true;
        }
        IGUIEnvironment* env;
        SAppContext Context;
};

bool onQuit(const irr::SEvent::SGUIEvent& event)
{
    printf("Pressed close button\n");
    return false;
}


/*
Ok, now for the more interesting part. First, create the Irrlicht device. As in
some examples before, we ask the user which driver he wants to use for this
example:
*/


int main()
{
    // ask user for driver
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;

    // create device and exit if creation failed

    IrrlichtDevice * device = createDevice(driverType, core::dimension2d<u32>(640, 480));

    if (device == 0)
        return 1; // could not create selected driver.

    /* The creation was successful, now we set the event receiver and
    	store pointers to the driver and to the gui environment. */

    device->setWindowCaption(L"Irrlicht Engine - User Interface Demo");
    device->setResizable(true);

    video::IVideoDriver* driver = device->getVideoDriver();
    IGUIEnvironment* env = device->getGUIEnvironment();

    /*
    To make the font a little bit nicer, we load an external font
    and set it as the new default font in the skin.
    To keep the standard font for tool tip text, we set it to
    the built-in font.
    */

    IGUISkin* skin = env->getSkin();
    IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
    if (font)
        skin->setFont(font);

    skin->setFont(env->getBuiltInFont(), EGDF_TOOLTIP);

    /*
    We add three buttons. The first one closes the engine. The second
    creates a window and the third opens a file open dialog. The third
    parameter is the id of the button, with which we can easily identify
    the button in the event receiver.
    */

    irr::gui::IGUIElement* quitButton = env->addButton(rect<s32>(10,240,110,240 + 32), 0, -1,
                                        L"Quit", L"Exits Program");
    irr::gui::IGUIElement* newWindowButton = env->addButton(rect<s32>(10,280,110,280 + 32), 0, -1,
            L"New Window", L"Launches a new Window");
    irr::gui::IGUIElement* fileopenButton = env->addButton(rect<s32>(10,320,110,320 + 32), 0, -1,
                                            L"File Open", L"Opens a file");

    /*
    Now, we add a static text and a scrollbar, which modifies the
    transparency of all gui elements. We set the maximum value of
    the scrollbar to 255, because that's the maximal value for
    a color value.
    Then we create an other static text and a list box.
    */

    env->addStaticText(L"Transparent Control:", rect<s32>(150,20,350,40), true);
    IGUIScrollBar* scrollbar = env->addScrollBar(true,
                               rect<s32>(150, 45, 350, 60), 0, -1);
    scrollbar->setMax(255);

    // set scrollbar position to alpha value of an arbitrary element
    scrollbar->setPos(env->getSkin()->getColor(EGDC_WINDOW).getAlpha());

    env->addStaticText(L"Logging ListBox:", rect<s32>(50,110,250,130), true);
    IGUIListBox * listbox = env->addListBox(rect<s32>(50, 140, 250, 210));
    env->addEditBox(L"Editable Text", rect<s32>(350, 80, 550, 100));

    // Store the appropriate data in a context structure.
    SAppContext context;
    context.device = device;
    context.counter = 0;
    context.listbox = listbox;

    // Then create the event receiver, giving it that context structure.
    MyEvents receiver(context);
    CGUIEventDispatcher dispatcher;

    //you can also bind simple functions
    dispatcher.bind(quitButton, onQuit, irr::gui::EGET_BUTTON_CLICKED);

    //Button clicks
    dispatcher.bind(quitButton, &receiver, &MyEvents::onQuit, irr::gui::EGET_BUTTON_CLICKED);
    dispatcher.bind(newWindowButton, &receiver, &MyEvents::onNewWindow, irr::gui::EGET_BUTTON_CLICKED);
    dispatcher.bind(fileopenButton, &receiver, &MyEvents::onFileOpenButton, irr::gui::EGET_BUTTON_CLICKED);

    //Scrollbar changed
    dispatcher.bind(scrollbar, &receiver, &MyEvents::onTransparentChanged, irr::gui::EGET_SCROLL_BAR_CHANGED);


    // And tell the device to use our custom event receiver.
    device->setEventReceiver(&dispatcher);


    /*
    And at last, we create a nice Irrlicht Engine logo in the top left corner.
    */
    env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
                  position2d<int>(10,10));


    /*
    That's all, we only have to draw everything.
    */

    while(device->run() && driver)
        if (device->isWindowActive())
        {
            driver->beginScene(true, true, SColor(0,200,200,200));

            env->drawAll();

            driver->endScene();
        }

    device->drop();

    return 0;
}
CGUIEventDispatcher.h

Code: Select all

#ifndef CGUIEVENTDISPATCHER_H
#define CGUIEVENTDISPATCHER_H

#include <IEventReceiver.h>
#include <irrArray.h>
#include <irrList.h>

class CGUIEventDispatcher : public irr::IEventReceiver
{
    public:
        /** Default constructor */
        CGUIEventDispatcher();
        /** Default destructor */
        virtual ~CGUIEventDispatcher();

        virtual bool OnEvent(const irr::SEvent& event);

        bool bind(irr::gui::IGUIElement* element, bool (*func)(const irr::SEvent::SGUIEvent&), irr::gui::EGUI_EVENT_TYPE type = irr::gui::EGET_COUNT);
        template<class T>
        bool bind(irr::gui::IGUIElement* element, T* c, bool (T::*func)(const irr::SEvent::SGUIEvent&), irr::gui::EGUI_EVENT_TYPE type = irr::gui::EGET_COUNT)
        {
            Storage::Iterator it = Bindings.begin();
            while(it != Bindings.end())
            {
                if ((*it) == element && (*it).checkClass(c))
                    return false;
                else if ((*it) == element)
                {
                    (*it).Data.push_back(new CMethodHandle<T>(c, func, type));
                    return true;
                }
                ++it;
            }
            Pair p;
            p.Key = element;
            p.Data.push_back(new CMethodHandle<T>(c, func, type));
            Bindings.push_back(p);
            return true;
        }

        void unbind(irr::gui::IGUIElement* element);
        void unbind(irr::gui::IGUIElement* element, bool (*func)(const irr::SEvent::SGUIEvent&));
        template<class T>
        void unbind(irr::gui::IGUIElement* element, T* c)
        {
            Storage::Iterator it = Bindings.begin();
            while(it != Bindings.end())
            {
                if ((*it) == element)
                {
                    bool r = (*it).releaseClass(c);
                    return;
                }
                ++it;
            }
        }
        template<class T>
        void unbind(T* c)
        {
            Storage::Iterator it = Bindings.begin();
            while(it != Bindings.end())
            {
                bool r = (*it).releaseClass(c);
                ++it;
            }
        }

    protected:
        typedef bool (*FuncHandle)(const irr::SEvent::SGUIEvent&);
        struct IHandle
        {
            IHandle(irr::gui::EGUI_EVENT_TYPE type = irr::gui::EGET_COUNT)
            {
                EventType = type;
            }
            virtual ~IHandle(void){};
            virtual bool call(const irr::SEvent::SGUIEvent& event) = 0;
            virtual bool checkFunc(FuncHandle f) const = 0;
            virtual bool checkClass(void* c) const = 0;
            virtual const irr::gui::EGUI_EVENT_TYPE& getType(void)const{return EventType;}
            irr::gui::EGUI_EVENT_TYPE EventType;
        };
        struct CFunctionHandle : IHandle
        {
            CFunctionHandle(FuncHandle f, irr::gui::EGUI_EVENT_TYPE type): IHandle(type), Func(f)
            {}
            bool call(const irr::SEvent::SGUIEvent& event)
            {
                if (getType() == irr::gui::EGET_COUNT || getType() == event.EventType)
                    return Func(event);
                return false;
            }
            bool checkFunc(FuncHandle f) const
            {
                return f == Func;
            }
            bool checkClass(void* c) const
            {
                return false;
            }
            FuncHandle Func;
        };
        template<class T>
        struct CMethodHandle : IHandle
        {
            typedef bool (T::*MethodHandle)(const irr::SEvent::SGUIEvent&);
            CMethodHandle(T* c, MethodHandle f, irr::gui::EGUI_EVENT_TYPE type): IHandle(type),Func(f), Class(c)
            {
            }
            bool call(const irr::SEvent::SGUIEvent& event)
            {
                if (getType() == irr::gui::EGET_COUNT || getType() == event.EventType)
                    return (Class->*Func)(event);
                return false;
            }
            bool checkFunc(FuncHandle f) const
            {
                return false;
            }
            bool checkClass(void* c) const
            {
                return Class == c;
            }
            MethodHandle Func;
            T* Class;
        };
        typedef irr::core::array<IHandle*> Link;
        struct Pair
        {
            Pair(void) : Key(0){}
            Pair (const Pair& other)
            {
                *this = other;
            }
            Pair& operator= (const Pair& other)
            {
                Key = other.Key;
                Data = other.Data;
                return *this;
            }
            bool operator==(const irr::gui::IGUIElement* key)
            {
                return Key == key;
            }
            bool call(const irr::SEvent::SGUIEvent& event)
            {
                for (irr::u32 i=0;i<Data.size();++i)
                {
                    if (Data[i]->call(event))
                        return true;
                }
                return false;
            }
            bool checkFunc(FuncHandle f)
            {
                for (irr::u32 i=0;i<Data.size();++i)
                {
                    if (Data[i]->checkFunc(f))
                        return true;
                }
                return false;
            }
            bool checkClass(void* c)
            {
                for (irr::u32 i=0;i<Data.size();++i)
                {
                    if (Data[i]->checkClass(c))
                        return true;
                }
                return false;
            }
            bool releaseFunc(FuncHandle f)
            {
                for (irr::u32 i=0;i<Data.size();++i)
                {
                    if (Data[i]->checkFunc(f))
                    {
                        delete Data[i];
                        Data.erase(i);
                        return true;
                    }
                }
                return false;
            }
            bool releaseClass(void* c)
            {
                for (irr::u32 i=0;i<Data.size();++i)
                {
                    if (Data[i]->checkClass(c))
                    {
                        delete Data[i];
                        Data.erase(i);
                        return true;
                    }
                }
                return false;
            }
            void releaseHandle(void)
            {
                for (irr::u32 i=0;i<Data.size();++i)
                {
                    delete Data[i];
                }
                Data.clear();
            }
            irr::gui::IGUIElement* Key;
            Link Data;
        };
        typedef irr::core::list<Pair> Storage;
        Storage Bindings;
    private:
};

#endif // CGUIEVENTDISPATCHER_H
CGUIEventDispatcher.cpp

Code: Select all

/*
  Copyright (C) 2011 Daniel Sudmann

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Daniel Sudmann suddani@googlemail.com
*/
#include "CGUIEventDispatcher.h"

CGUIEventDispatcher::CGUIEventDispatcher()
{
    //ctor
}

CGUIEventDispatcher::~CGUIEventDispatcher()
{
    //dtor
}

bool CGUIEventDispatcher::OnEvent(const irr::SEvent& event)
{
    if (event.EventType == irr::EET_GUI_EVENT)
    {
        irr::gui::IGUIElement* caller = event.GUIEvent.Caller;
        Storage::Iterator it = Bindings.begin();
        while(it != Bindings.end())
        {
            if ((*it) == caller)
                return (*it).call(event.GUIEvent);
            ++it;
        }
    }
    return false;
}

bool CGUIEventDispatcher::bind(irr::gui::IGUIElement* element, bool (*func)(const irr::SEvent::SGUIEvent&), irr::gui::EGUI_EVENT_TYPE type)
{
    Storage::Iterator it = Bindings.begin();
    while(it != Bindings.end())
    {
        if ((*it) == element && (*it).checkFunc(func))
            return false;
        else if ((*it) == element)
        {
            (*it).Data.push_back(new CFunctionHandle(func, type));
            return true;
        }
        ++it;
    }
    Pair p;
    p.Key = element;
    p.Data.push_back(new CFunctionHandle(func, type));
    Bindings.push_back(p);
    return true;
}

void CGUIEventDispatcher::unbind(irr::gui::IGUIElement* element)
{
    Storage::Iterator it = Bindings.begin();
    while(it != Bindings.end())
    {
        if ((*it) == element)
        {
            (*it).releaseHandle();
            Bindings.erase(it);
            return;
        }
        ++it;
    }
}

void CGUIEventDispatcher::unbind(irr::gui::IGUIElement* element, bool (*func)(const irr::SEvent::SGUIEvent&))
{
    Storage::Iterator it = Bindings.begin();
    while(it != Bindings.end())
    {
        if ((*it) == element)
        {
            bool r = (*it).releaseFunc(func);
            return;
        }
        ++it;
    }
}
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
REDDemon
Developer
Posts: 1044
Joined: Tue Aug 31, 2010 8:06 pm
Location: Genova (Italy)

Post by REDDemon »

Nice snippet :) would be very usefull in certain cases and it is a good C++ tutorial also. I will take a try with it :)
Junior Irrlicht Developer.
Real value in social networks is not about "increasing" number of followers, but about getting in touch with Amazing people.
- by Me
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

REDDemon wrote:Nice snippet :) would be very usefull in certain cases and it is a good C++ tutorial also. I will take a try with it :)
First of all thanks :D
But what ya mean with certain cases? Actually that is how every GUI lib out there works...more or less...of course their event systems have security checks in it to allow deleting a registered class with automaticly removing all its bindings...but yeah that would have made the snippet overly complex and i am sure everyone has its own way of doing those things.
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Post Reply