adding function pointer to IGUIButton

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
euler
Posts: 4
Joined: Sun Jun 21, 2009 2:04 am

adding function pointer to IGUIButton

Post by euler »

I am putting a lot of buttons / checkbox / scrollbar / ... and keeping track of them in an event manager is a pain because I must add a long switch/case for each id. The idea I got is to modify the class through inheritance with the following simplified scheme :

Code: Select all

class Button : public irr::gui::IGUIButton {
  void (*function)();
public:
  Button(args):IGUIButton(args){}
  ~Button(){}
  static void runfunction(Button *param){param->function();}
};
In that case the event manager would only need a call to Button::runfunction. But the problem I get is the class irr::gui::IGUIButton is not designed for inheritance. It is abstract, the function addbutton would not work and it would need a dynamic_cast in the event manager. There are some solutions to that but they are not really nice. I would please like to know if an elegant method is used in that case of if not, how to adjust the inheritance.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Post by Seven »

you could catch any button press in your main event receiver and then get the gui element, cast it to your derived button, and call the run function

personally, i wrote my event handler like this so that I can override the gui events more easily... I then derive all classes from it, including levels and objects (they can receieve gui input) and the main event receiver just sends the message along the pipeline...

Code: Select all

class IrrEventHandler : public IEventReceiver
{
public:

	// Enumeration for UP, DOWN, PRESSED and RELEASED key states. Also used for mouse button states.
	enum keyStatesENUM {UP, DOWN, PRESSED, RELEASED};
	
	// Keyboard key states.
	keyStatesENUM keyState[KEY_KEY_CODES_COUNT];

	// Mouse data.
	struct mouseData
	{
		int X;
		int Y;
		bool LButtonDown;
		bool RButtonDown;
		bool MButtonDown;
		float Wheel; 
		float WheelDelta;
	};

	struct mouseData m_Mouse;
	int MouseX()			{ return m_Mouse.X;				};
	int MouseY()			{ return m_Mouse.Y;				};
	int MouseWheel()		{ return m_Mouse.Wheel;			};
	int MouseWheelDelta()	{ return m_Mouse.WheelDelta;	};

	IrrEventHandler(void)			
	{
		for (int i = 0; i < KEY_KEY_CODES_COUNT; i++) keyState[i] = RELEASED;
		m_Mouse.X = 0;
		m_Mouse.Y = 0;
		m_Mouse.Wheel = 0;
		m_Mouse.WheelDelta = 0;
		m_Mouse.LButtonDown = false;
		m_Mouse.RButtonDown = false;
		m_Mouse.MButtonDown = false;
	};

	virtual ~IrrEventHandler(void)	{};

	// keyboard events
	virtual bool OnKeyInputEvent(const SEvent& e)	{	return false;														};
	virtual bool IsKeyPressed(char keycode)			{	return (keyState[keycode] == PRESSED);								};
	virtual bool IsKeyDown(char keycode)			{	return (keyState[keycode] == DOWN || keyState[keycode] == PRESSED); };
	virtual bool IsKeyUp(char keycode)				{	return (keyState[keycode] == UP || keyState[keycode] == RELEASED);	};
	virtual bool IsKeyReleased(char keycode)		{	return (keyState[keycode] == RELEASED);								};

	// guievents
	virtual bool OnButtonClicked(const SEvent& e)			{	return false;	};
	virtual bool OnScrollBarChanged(const SEvent& e)		{	return false;	};
	virtual bool OnCheckBoxChanged(const SEvent& e)			{	return false;	};
	virtual bool OnListBoxChanged(const SEvent& e)			{	return false;	};
	virtual bool OnListBoxSelectedAgain(const SEvent& e)	{	return false;	};
	virtual bool OnFileSelected(const SEvent& e)			{	return false;	};
	virtual bool OnMessageBoxYes(const SEvent& e)			{	return false;	};
	virtual bool OnMessageBoxNo(const SEvent& e)			{	return false;	};
	virtual bool OnMessageBoxOk(const SEvent& e)			{	return false;	};
	virtual bool OnMessageBoxCancel(const SEvent& e)		{	return false;	};
	virtual bool OnEditBoxEnter(const SEvent& e)			{	return false;	};
	virtual bool OnTabChanged(const SEvent& e)				{	return false;	};
	virtual bool OnComboBoxChanged(const SEvent& e)			{	return false;	};
	virtual bool OnSpinBoxChanged(const SEvent& e)			{	return false;	};

	// mouse events
	virtual bool OnLMousePressedDown(const SEvent& e)		{	return false;	};
	virtual bool OnRMousePressedDown(const SEvent& e)		{	return false;	};
	virtual bool OnMMousePressedDown(const SEvent& e)		{	return false;	};
	virtual bool OnLMouseLeftUp(const SEvent& e)			{	return false;	};
	virtual bool OnRMouseLeftUp(const SEvent& e)			{	return false;	};
	virtual bool OnMMouseLeftUp(const SEvent& e)			{	return false;	};
	virtual bool OnMouseMoved(const SEvent& e)				{	return false;	};
	virtual bool OnMouseWheel(const SEvent& e)				{	return false;	};

	// user events
	virtual bool OnUserEvent(const SEvent& e)				{	return false;	};

	virtual bool OnEvent(const SEvent& e)
	{
		switch (e.EventType)
		{
			case EET_KEY_INPUT_EVENT : 
				{
					if (e.KeyInput.PressedDown == true)
					{
						if (keyState[e.KeyInput.Key] != DOWN) 
							keyState[e.KeyInput.Key] = PRESSED; 
						else keyState[e.KeyInput.Key] = DOWN; 
					}
					else
					if (keyState[e.KeyInput.Key] != UP)	
						keyState[e.KeyInput.Key] = RELEASED; 

					if (e.KeyInput.PressedDown == true) return OnKeyInputEvent(e); 
				} 	break;

			case EET_GUI_EVENT :
				{
					switch (e.GUIEvent.EventType)
					{
						case EGET_BUTTON_CLICKED			: return OnButtonClicked(e);
						case EGET_SCROLL_BAR_CHANGED		: return OnScrollBarChanged(e);
						case EGET_CHECKBOX_CHANGED			: return OnCheckBoxChanged(e);
						case EGET_LISTBOX_CHANGED			: return OnListBoxChanged(e);
						case EGET_LISTBOX_SELECTED_AGAIN	: return OnListBoxSelectedAgain(e);
						case EGET_FILE_SELECTED				: return OnFileSelected(e);
						case EGET_MESSAGEBOX_YES			: return OnMessageBoxYes(e);
						case EGET_MESSAGEBOX_NO				: return OnMessageBoxNo(e);
						case EGET_MESSAGEBOX_OK				: return OnMessageBoxOk(e);
						case EGET_MESSAGEBOX_CANCEL			: return OnMessageBoxCancel(e);
						case EGET_EDITBOX_ENTER				: return OnEditBoxEnter(e);
						case EGET_TAB_CHANGED				: return OnTabChanged(e);
						case EGET_COMBO_BOX_CHANGED			: return OnComboBoxChanged(e);
						case EGET_SPINBOX_CHANGED			: return OnSpinBoxChanged(e);
						default : return false;
					}
				} break; 

			case EET_MOUSE_INPUT_EVENT :
				{
					m_Mouse.X = e.MouseInput.X;
					m_Mouse.Y = e.MouseInput.Y;

					switch (e.MouseInput.Event)
					{
						case EMIE_LMOUSE_PRESSED_DOWN	:	m_Mouse.LButtonDown = true; return OnLMousePressedDown(e);
						case EMIE_RMOUSE_PRESSED_DOWN	:	m_Mouse.RButtonDown = true; return OnRMousePressedDown(e);
						case EMIE_MMOUSE_PRESSED_DOWN	:	m_Mouse.MButtonDown = true; return OnMMousePressedDown(e);
						case EMIE_LMOUSE_LEFT_UP		:	m_Mouse.LButtonDown = false; return OnLMouseLeftUp(e);
						case EMIE_RMOUSE_LEFT_UP		:	m_Mouse.RButtonDown = false; return OnRMouseLeftUp(e);
						case EMIE_MMOUSE_LEFT_UP		:	m_Mouse.MButtonDown = false; return OnMMouseLeftUp(e);
						case EMIE_MOUSE_MOVED			:	return OnMouseMoved(e);
						case EMIE_MOUSE_WHEEL			:	
							{
								m_Mouse.WheelDelta = m_Mouse.Wheel - e.MouseInput.Wheel; 
								m_Mouse.Wheel += e.MouseInput.Wheel;
								return OnMouseWheel(e);
							}
						default : return false;
					}
				} break;

			case EET_USER_EVENT : 
				{
					return OnUserEvent(e);
					default : return false;
				} break;
		}
		return false;
	}
};
euler
Posts: 4
Joined: Sun Jun 21, 2009 2:04 am

Post by euler »

Seven wrote:you could catch any button press in your main event receiver and then get the gui element, cast it to your derived button, and call the run function
That is exactly what I am trying to do. The problem is actually making the derived button is a real pain. The engine wont allow me to do it.
euler
Posts: 4
Joined: Sun Jun 21, 2009 2:04 am

Post by euler »

Got something new, the reason why it is difficult. The IGUIButton class is abstract, the class used in addbutton is CGUIButton, but this one is only in the source code. CGUIButton.h is not in the includes, I added it but the virtual functions are not found by the linker, which means it is not in the library. I cannot really include the cpp since it would mess up the whole engine (lots of includes in it).
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

well, in this case you'll have to make the changes in the code files (CGUIButton.h, CGUIButton.cpp and IGUIButton.h) and recompile the engine... ;)
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
CuteAlien
Admin
Posts: 10060
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

You can avoid the long switch-case stuff by handling function pointers automatic on application side. Personally I used functors for that which are called in the eventreceiver.

But you are right that CGUIButton.h is not thought for inheritance outside the engine. IGUIButton is. But you have the sources of CGUIButton, so it is not really a problem. Copy both CGUIButton files in your application. Rename them. Use some search&replace to also rename the classnames in that files. And finished is your new button class in which you can now change whatever you need. If you additionally need serialization for your own gui-classes you also have to register a IGUIElementFactory which can create your own classes.
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
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Post by Seven »

euler wrote:Got something new, the reason why it is difficult. The IGUIButton class is abstract, the class used in addbutton is CGUIButton, but this one is only in the source code. CGUIButton.h is not in the includes, I added it but the virtual functions are not found by the linker, which means it is not in the library. I cannot really include the cpp since it would mess up the whole engine (lots of includes in it).
ok, just rying to think outside here. you could create a small structure that holds the gui object's ID and a pointer to the function you want to call. then, in the event handler, get the gui element ID, search a list of the small structures, and then call the stored Run() function. when you wnat to create a gui object, call a fuction like,

ADD_GUI_ELEMENT(IUIElement* e, functionToCall)
{
myarray[index].guiElement = e;
myarry[index].function = functiontocall;
}

it aint pretty though.... it aint pretty.....
euler
Posts: 4
Joined: Sun Jun 21, 2009 2:04 am

Post by euler »

CuteAlien wrote:You can avoid the long switch-case stuff by handling function pointers automatic on application side. Personally I used functors for that which are called in the eventreceiver.
That seems the best solution avalaible. I tried something like that, which seems to be ok for what I tried.

Code: Select all

class EventManager : public irr::IEventReceiver
{
	irr::IrrlichtDevice *device;
	bool KeyIsDown[irr::KEY_KEY_CODES_COUNT];
	irr::core::array<void (*)(irr::IrrlichtDevice *)> guifunctions;
public
	EventManager(irr::IrrlichtDevice *dev);
	~EventManager();
	virtual bool OnEvent(const irr::SEvent& event);
	virtual bool IsKeyDown(irr::EKEY_CODE keyCode) const;
	int addgui(void (*f)(irr::IrrlichtDevice *));
};

int EventManager::addgui(void (*f)(irr::IrrlichtDevice *))
{
	int j=guifunctions.size();
	for(int i=0;i<j;i++){
		if(guifunctions[i]==f)return i;
	}
	guifunctions.push_back(f);
	return j;
}
It is then easy to add a button :

Code: Select all

guienv->addButton(rect<s32>(10,240,110,240 + 32), 0,eventmanager.addgui(&function),L"dofunction", L"runs the function");
And the call in the event receiver is cleaner :

Code: Select all

if(guifunctions[id])(guifunctions[id])(device);
Of course this code can be improved but it do the job.
Post Reply