Page 1 of 1

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

Posted: Sun Apr 26, 2009 7:45 am
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.

Posted: Sun May 03, 2009 5:27 am
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.

Posted: Mon May 04, 2009 2:37 am
by Acki
right, post a demo code that reproduces the error, please... :roll:

Posted: Tue May 05, 2009 6:49 pm
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.

Posted: Wed May 06, 2009 8:55 am
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).

Posted: Sun May 24, 2009 10:31 pm
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.

Posted: Mon Jun 01, 2009 4:04 am
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.