[fixed]Probably a bug in GUI (Menu click handling)

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
HackLeX
Posts: 5
Joined: Sun Apr 26, 2009 7:18 am

[fixed]Probably a bug in GUI (Menu click handling)

Post by HackLeX »

To reproduce the effect, create a menu,
then make a toolbar with a toolbutton right under
the menu item. Then pick a menu item while holding
mouse over the toolbutton -- the tool button will be
clicked as well as the menu command.

...
Right after the menu command is selected, the MouseDown
event is also caught by the toolbar button located at cursor
position.
...
Also, usual windows menus works with a single click
(MouseDown selects the menu, MouseUp picks up a command)
while the IrrLicht menu reacts only on MouseDown.
CuteAlien
Admin
Posts: 9693
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

I think there are still issues with menus. But if you report something like that it always helps to add a little program which others can compile & run for reproducing the problem. Also you can put bugs on the bugtracker to make sure they don't get lost or ignored.
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
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

right, post a demo code that reproduces the error, please... :roll:
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
HackLeX
Posts: 5
Joined: Sun Apr 26, 2009 7:18 am

Post by HackLeX »

Okay, here is an example.
Here is an important note -- this bug is only noticeable against pushbuttons.
Here goes the code

Code: Select all

// IrrGuiBug.cpp : Defines the entry point for the console application.
#pragma region Header linking
#include "stdafx.h"
#include <irrlicht.h>
#include <windows.h>
#include <iostream>
#include <fstream>
using namespace irr;
using namespace core;
using namespace gui;
using namespace io;
using namespace scene;
using namespace video;

#pragma comment(lib, "Irrlicht.lib")
#pragma comment(lib, "user32.lib")
#pragma endregion

#pragma region GUI Definitions
#define SCREEN_H 768
#define SCREEN_W 1024


#define ID_TOOLBAR 999
#define ID_MENUBAR 1000
#define ID_FILE_OPEN 101
#define ID_FILE_CLOSE 102
#define ID_FILE_EXIT 103
#define ID_TOOL_OPEN 2001
#define ID_TOOL_CLOSE 2002
#pragma endregion

#pragma region global variables
IrrlichtDevice *device;
video::IVideoDriver* driver;
scene::ISceneManager* smgr; 
gui::IGUIEnvironment* env; 
wchar_t *cap ;
#pragma endregion

#pragma region Event receiver
class MyEventReceiver : irr::IEventReceiver{
public:
	virtual bool OnEvent(const SEvent& event){
		if (event.EventType == EET_GUI_EVENT)
		{			
			s32 id = event.GUIEvent.Caller->getID();
			switch(event.GUIEvent.EventType){
			case EGET_BUTTON_CLICKED:	
				if(wcslen(cap)>50) cap[0]=0;
				if(id==ID_TOOL_OPEN){
					wcscat_s(cap,100,L"Open Tool ");					
				}
				if(id==ID_TOOL_CLOSE){
					wcscat_s(cap,100,L"Close Tool ");
				}
				device->setWindowCaption(cap);
				break;
			case EGET_MENU_ITEM_SELECTED:
				IGUIContextMenu* menu = (IGUIContextMenu*)event.GUIEvent.Caller;
				s32 iid = menu->getItemCommandId(menu->getSelectedItem());    
				if(wcslen(cap)>50) cap[0]=0;
				if(iid==ID_FILE_OPEN) {
					wcscat_s(cap,100,L"Open Menu ");	
				}
				if(iid==ID_FILE_CLOSE) {
					wcscat_s(cap,100,L"Close Menu ");	
				}
				if(iid==ID_FILE_EXIT) {
					device->closeDevice(); 
				}
				device->setWindowCaption(cap);
				break;
			}
		}
		return 0;
	}

	MyEventReceiver()
	{/*_*/} //fun::smile
};

#pragma endregion

int _tmain(int argc, _TCHAR* argv[])
{
	#pragma region Startup Driver Selector Routine
	video::E_DRIVER_TYPE driverType;
	printf("Please select the driver you want for this example:\n"\
		" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
		" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
		" (f) NullDevice\n (otherKey) exit\n\n");
	char i='a';
	if(GetAsyncKeyState(VK_SHIFT)!=0)
		std::cin >> i;
	switch(i)
	{
		case 'a': driverType = video::EDT_DIRECT3D9;		break;
		case 'b': driverType = video::EDT_DIRECT3D8;		break;
		case 'c': driverType = video::EDT_OPENGL;			break;
		case 'd': driverType = video::EDT_SOFTWARE;			break;
		case 'e': driverType = video::EDT_BURNINGSVIDEO;	break;
		case 'f': driverType = video::EDT_NULL;				break;
		default: return 1;
	}	
	
	// create device and exit if creation failed
	core::dimension2di videoDim ( 1024,768 );

	device = createDevice(driverType, videoDim, 32, FALSE );

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

	#pragma endregion

	#pragma region common initialization
	cap = new wchar_t[100];
	driver = device->getVideoDriver();
	smgr = device->getSceneManager();
	env = device->getGUIEnvironment();
	#pragma endregion

	#pragma region GUI Initialization
	IGUIContextMenu *menubar = env->addMenu(0,ID_MENUBAR);
	IGUIContextMenu *file_menu = menubar->getSubMenu(menubar->addItem(L"File",-1,true,true));
	
	file_menu->addItem(L"Open...",ID_FILE_OPEN,1);
	file_menu->addItem(L"Close file",ID_FILE_CLOSE,1);
	file_menu->addSeparator();
	file_menu->addItem(L"Quit viewer",ID_FILE_EXIT,true);	
	
	IGUIToolBar *toolbar = env->addToolBar(0,-1);

	toolbar->setRelativePosition(rect<s32>(0,
		menubar->getAbsoluteClippingRect().getHeight(),
		640,
		48));

	toolbar->setVisible(true);

	IGUIButton *open_tool = env->addButton(rect<s32>(2,2,25,24),
		toolbar,ID_TOOL_OPEN,L"Open",L"Open...");
	
	open_tool->setUseAlphaChannel(true);
	open_tool->setIsPushButton(true);
	open_tool->setPressed(true);
	
	IGUIButton * close_tool = env->addButton(rect<s32>(26,2,26+23,24),
		toolbar,ID_TOOL_CLOSE,L"Close",L"Close");
	
	close_tool->setUseAlphaChannel(true);
	close_tool->setIsPushButton(true);
	close_tool->setPressed(true);

	//Opaque GUI
	for (u32 i=0; i<EGDC_COUNT ; ++i)
	{
		SColor col = env->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
		col.setAlpha(255 );
		env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
	}
	#pragma endregion

	//Event catcher creation
	IEventReceiver *receiver =(IEventReceiver*)( new MyEventReceiver());
	device->setEventReceiver(receiver);

	//main loop
	while(device->run())
	if (device->isWindowActive())
	{
		driver->beginScene(true, true, video::SColor(255,40,40,80));
		smgr->drawAll();
		env->drawAll();
		driver->endScene();
	}
	return 0;
}




Just select a menu item while your mouse cursor is positioned right over the pushbutton, and watch the output in the window caption.
eye776
Posts: 94
Joined: Sun Dec 28, 2008 11:07 pm

Post by eye776 »

Bug confirmed here as well. It also appears if you have an IGUITabControl under a menu (The tab under the menu becomes active).
CuteAlien
Admin
Posts: 9693
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

Thanks, I can reproduce it and added it to the bugtracker. I have to look some more into this, as the menu already got reworked several times and still has a lot of problems. I guess making them behave like typical windows menus is a good idea anyway.
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
CuteAlien
Admin
Posts: 9693
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

OK, I have added a fix to svn in trunk and 1.5 branch.

I would be glad if anyone else could test new behaviour as there is really a lot of stuff that can go wrong. My test-code is below (I will probably add it later to irrlicht-tests, but don't know yet in which folder I should put manual tests):

Code: Select all

#include <irrlicht.h>
#include <iostream>

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

enum 
{
	E_RIGHT_CLICK_MENUES = 1,
	E_LEFT_CLICK_AREA,
};

struct SAppContext
{
	IrrlichtDevice * Device;
	gui::IGUIEnvironment* Environment;
	gui::IGUIStaticText* LeftClickArea;
	gui::IGUIStaticText* StatusBar;
	gui::IGUIButton * CreateRightClickMenues;
};

void FillMenu( gui::IGUIContextMenu* menu )
{
	menu->addItem(L"top1", -1, true, true);
	menu->addItem(L"top2", -1, true, true);
	menu->addItem(L"no sub", -1, true, false);
	IGUIContextMenu* submenu = menu->getSubMenu(0);
	if ( submenu )
	{
        submenu->addItem(L"item1", -1);
        submenu->addSeparator();
        submenu->addItem(L"item2", -1);
        submenu->addItem(L"checked", -1, true, false, true);
        submenu->addItem(L"sub1", -1, true, true, false );
        submenu->addItem(L"sub2", -1, true, true, false );
		submenu->addItem(L"disabled", -1, false, false, false);
	}
	IGUIContextMenu* subsubmenu = submenu->getSubMenu(4);
	if ( subsubmenu )
	{
        subsubmenu->addItem(L"subitemA", -1);
        subsubmenu->addItem(L"subitemB", -1);
	}
	subsubmenu = submenu->getSubMenu(5);
	if ( subsubmenu )
	{
		subsubmenu->addItem(L"subitemC", -1);
		subsubmenu->addItem(L"subitemD", -1);
	}
	
	submenu = menu->getSubMenu(1);
	if ( submenu )
	{
        submenu->addItem(L"item", -1);
	}	
}

class MyEventReceiver : public IEventReceiver
{
public:
	MyEventReceiver(SAppContext & context) : Context(context), ContextMenu(0) { }
	
	void ShowContextMenu(irr::s32 x, irr::s32 y)
	{
		core::rect<s32> rect(x, y, x+50, y+50);
		ContextMenu = Context.Environment->addContextMenu (rect);
		FillMenu(ContextMenu);
	};

	virtual bool OnEvent(const SEvent& event)
	{
		if (event.EventType == EET_MOUSE_INPUT_EVENT )
		{
			if ( event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN )
			{
				if ( !Context.LeftClickArea->isPointInside( core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y) ) 
				     && Context.CreateRightClickMenues->isPressed()
				   )
				{
					ShowContextMenu(event.MouseInput.X, event.MouseInput.Y);
				}
			}
			if ( event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP )
			{
				Context.StatusBar->setText(L"");
				if ( Context.LeftClickArea->isPointInside( core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y) ) )
				{
					ShowContextMenu(event.MouseInput.X, event.MouseInput.Y);
					return true;
				}

			}
		}
		if (event.EventType == EET_GUI_EVENT)
		{
			s32 id = event.GUIEvent.Caller->getID();
			switch(event.GUIEvent.EventType)
			{
			case EGET_MENU_ITEM_SELECTED:
			{
				IGUIContextMenu * menu = static_cast<IGUIContextMenu*>(event.GUIEvent.Caller);
				s32 idx = menu->getSelectedItem();
				if ( idx >= 0 )
					Context.StatusBar->setText(menu->getItemText(idx));
				else
					Context.StatusBar->setText(L"selected event, but no item selected - should never happen.");
				break;
			}
			default:
				break;
			}
		}

		return false;
	}

private:
	SAppContext & Context;
	gui::IGUIContextMenu* ContextMenu;
};


int main()
{
	video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
	//IrrlichtDevice * device = createDevice(driverType, core::dimension2d<s32>(640, 480));	// <= irrlicht 1.5
	IrrlichtDevice * device = createDevice(driverType, core::dimension2d<u32>(640, 480));	// >= irrlicht 1.6
	if (device == 0)
		return 1; // could not create selected driver.

	video::IVideoDriver* driver = device->getVideoDriver();
	IGUIEnvironment* env = device->getGUIEnvironment();
	
	SAppContext context;
	context.Device = device;
	context.Environment = env;
	
	// menu bar in main window
	gui::IGUIContextMenu* menuBar = env->addMenu (0, -1);
	FillMenu(menuBar);
	
	// some dummy button below menu to see if mouse-clicks are correctly eaten by the menu
	core::rect<s32> rectBtn(0, 15, 150, 90);
	context.CreateRightClickMenues = env->addButton (rectBtn, 0, E_RIGHT_CLICK_MENUES, L"right-click context menu");
	context.CreateRightClickMenues->setIsPushButton(true);
	
	// menu bar in sub-window
	core::rect<s32> rectWnd(10, 100, 300, 200);
	IGUIWindow * wnd = env->addWindow (rectWnd, false, L"window", NULL, -1);
	gui::IGUIContextMenu* menuBar2 = env->addMenu (wnd, -1);
	FillMenu(menuBar2);
		
	core::rect<s32> rectStatic(10, 359, 150, 400);
	context.LeftClickArea = env->addStaticText (L"left click menu", rectStatic, true, true, 0, E_LEFT_CLICK_AREA, true);
	
	core::rect<s32> rectStatusBar(0, 450, 640, 480);
	context.StatusBar = env->addStaticText (L"", rectStatusBar, true, false);
	
	MyEventReceiver receiver(context);
	device->setEventReceiver(&receiver);
	
	while(device->run() && driver)
	{
		if (device->isWindowActive())
		{
			driver->beginScene(true, true, SColor(0,200,200,200));
	
			env->drawAll();
		
			driver->endScene();
		}
	}

	device->drop();

	return 0;
}
A few things which I already know about and about which I will probably care at some time are:
- Putting menus in IGUIWindow looks ugly (because we have no clientRect areas in gui-elements so far).
- Missing an automatic check-flag
- Context menus removing themselves is probably not always ok, that might need another option (but I need some more tests for that)

If anyone sees more problems with menus, please tell me.

edit: fixed a problem I had with left-clicks.
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
Post Reply