Alternatives to nested switch-case for event handling?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
carlwryker
Posts: 22
Joined: Sun Jun 12, 2016 8:03 am

Alternatives to nested switch-case for event handling?

Post by carlwryker »

My OnEvent() function has too many levels of nested switch-case statements. Currently, I have factored case bodies into helper functions. What alternatives/strategies should I explore to reduce the complexity of nested switch-case statements?

Code: Select all

 
/// Detects GUI events and determines actions.
    bool EditorGui::OnEvent( const irr::SEvent& event )
    {
 
        if ( event.EventType == irr::EET_GUI_EVENT )
        {
            // Detects gui id
            callerId = event.GUIEvent.Caller->getID();
 
            /// Detects events
            switch( event.GUIEvent.EventType )
            {
                /// If Menu Bar item was clicked
                case irr::gui::EGET_MENU_ITEM_SELECTED :
                {
                    return onMenuItemSelected( ( irr::gui::IGUIContextMenu* )event.GUIEvent.Caller );
                }
 
                /// If any button was clicked
                case irr::gui::EGET_BUTTON_CLICKED :
                {
                    return onButtonClicked( callerId );
                }
 
                /// Detect if a list item was double clicked
                case irr::gui::EGET_LISTBOX_SELECTED_AGAIN :
                {
                    return onListBoxSelectedAgain( callerId );
                }
 
                /// Detect if a table was double clicked
                case irr::gui::EGET_TABLE_SELECTED_AGAIN :
                {
                    return onTableSelectedAgain( callerId );
                }
 
                /// Detect tab that has mouse hover focus
                case irr::gui::EGET_ELEMENT_HOVERED :
                {
                    return onElementHovered( callerId );
                }
 
                /// Detect if tab control has changed
                case irr::gui::EGET_TAB_CHANGED :
                {
                    return onTabChanged();
                }
 
                default :
                    return false;
            }
        }
 
        return false;
 
    }
 
Example of helper function:

Code: Select all

 
/// Does stuff when a Menu Bar item is selected.
    bool EditorGui::onMenuItemSelected( irr::gui::IGUIContextMenu* pMenuItem )
    {
        irr::s32 menuItemId = pMenuItem->getItemCommandId( pMenuItem->getSelectedItem() ); // Get id of calling menu item
 
        switch( menuItemId )
        {
            case GUID_FILE_NEWPROJECT :
            {
                return onFileNewProject();
            }
 
            case GUID_FILE_OPENPROJECT :
            {
                return onFileOpenProject();
            }
 
            case GUID_FILE_SAVEPROJECT :
            {
                return onFileSaveProject();
            }
 
            case GUID_FILE_EXIT :
            {
                onFileExit();
            }
 
            default :
                return false;
        }
    }
 
Code::Blocks
mingw
Windows 10 64-bit
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Alternatives to nested switch-case for event handling?

Post by CuteAlien »

First - as long as the project is small it's not even so bad having a long case/switch as you know at least exactly where to look for events :-)

But when it gets larger I also use other strategies. Simplest one is - use several event-receivers which you call from your main event-receiver. I generally have one such eventhandler per ui-dialog. Where by dialog I mean - a group of active ui-elements (which usually have a common background). You can then decide to always call all of them - or only for your active dialog (which can be one or several - in my case it's usually one). Example here: http://irrlicht.sourceforge.net/forum// ... hp?t=26176

You can also use functors for your events, was a little discussed in this thread: http://irrlicht.sourceforge.net/forum/v ... =1&t=27585

I kinda use those in my racer game. Check https://bitbucket.org/mzeilfelder/trunk_hc1/src the src/gui_dialog.cpp/.h for my main dialog class and you might also take a look at my main event receiver which is in src/event_receiver_base.cpp/.h
Examples with code for my dialogs themself are in src/gui_dialogs sub-folder.

And the most comfortable (but also slowest) solution would be writing a signal/slot system on top of events like Qt has. But I have no example for that yet.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: Alternatives to nested switch-case for event handling?

Post by Mel »

I have a dynamic event receiver, it can grow and shrink as i see fit. It is very simple, and maybe not very efficient, but does the job. I suggest you to use the least receivers possible, but don't underestimate it either. 20-30 shouldn't be a problem even for real time

Header:

Code: Select all

#ifndef _DYNAMICEVENTRECEIVER_H_
#define _DYNAMICEVENTRECEIVER_H_
 
#include <irrlicht.h>
 
//This is a dynamic event receiver. The idea behind is to have an extensible receiver which can be enlarged or reduced at will, and which can vary its behavior in run time.
//The usage is as follows: you set this as the main receiver of the irrlicht device, and then, you can register and/or remove event receivers from it. The receivers just have to follow
//the same rules as Irrlicht receivers, if a receiver wants to end the processing of an event, it must return true, otherwise, it must return false. If an event returns true, the
//dynamic receiver returns immediately.
 
//Serves for instance, to remove certain routines from memory when certain parts of the program are reached and change them for others, simplifying its design, or to build 
//complex behaviors through the usage of simpler event receivers which otherwise, could create an event receiver very complex to maintain, or even, to write.
 
    class dynamicEventReceiver : public irr::IEventReceiver
    {
        irr::core::list<IEventReceiver*> receivers;
    public:
        //DTOR
        ~dynamicEventReceiver();
 
        //EVENT manager
        virtual bool OnEvent(const irr::SEvent& event);
 
        //Registers a new event receiver, returns the index of the newly inserted receiver, useful for instance if we want to delete it
        irr::s32 registerEventReceiver(IEventReceiver* rec);
 
        //Unregisters an event receiver by receiver pointer value
        void unregisterEventReceiver(IEventReceiver* rec);
 
        //Unregisters an event receiver by its index
        void unregisterEventReceiver(irr::u32);
    };
 
#endif
And code

Code: Select all

#include "dynamicEventReceiver.h"
 
using namespace irr;
 
 
    dynamicEventReceiver::~dynamicEventReceiver()
    {
        receivers.clear();
    }
 
    bool dynamicEventReceiver::OnEvent(const SEvent& event)
    {
        core::list<IEventReceiver*>::Iterator IT;
        IT = receivers.begin();
        bool result = false;
 
        while (!result && IT != receivers.end())
        {
            result = (*IT)->OnEvent(event);
            if (!result)
                ++IT;
        }
 
        return IT != receivers.end();
    }
 
    s32 dynamicEventReceiver::registerEventReceiver(IEventReceiver* rec)
    {
        s32 result = receivers.size();
        receivers.push_back(rec);
        return result;
    }
 
    void dynamicEventReceiver::unregisterEventReceiver(IEventReceiver* rec)
    {
        core::list<IEventReceiver*>::Iterator IT;
        IT = receivers.begin();
        while ((*IT) != rec && IT != receivers.end())
            ++IT;
        if (IT != receivers.end())
            receivers.erase(IT);
    }
 
    void dynamicEventReceiver::unregisterEventReceiver(irr::u32 index)
    {
        if (index < receivers.size())
        {
            core::list<IEventReceiver*>::Iterator IT;
            IT = receivers.begin() + index;
            receivers.erase(IT);
        }
    }
 
 
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
carlwryker
Posts: 22
Joined: Sun Jun 12, 2016 8:03 am

Re: Alternatives to nested switch-case for event handling?

Post by carlwryker »

Thank you!
Code::Blocks
mingw
Windows 10 64-bit
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: Alternatives to nested switch-case for event handling?

Post by Mel »

I hope it is useful to you :)
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: Alternatives to nested switch-case for event handling?

Post by chronologicaldot »

Depending on the complexity of the GUI system in my program, I usually create wrapper GUI elements that do all of the event handling for their children. This is faster than the event dispatch but it may require more work.
Post Reply