Adding Double-Click functionality (code patch)

A forum to store posts deemed exceptionally wise and useful
Post Reply
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Adding Double-Click functionality (code patch)

Post by jox »

I have implemented a (left mouse button) double click functionality into the irrlicht core.

It feels/behaves (ms)windows like:

The first click will always fire a EMIE_LMOUSE_PRESSED_DOWN. If the button is pressed down again within the timeout (speed) then also a EMIE_LMOUSE_DOUBLE_CLICKED (new event!) will be fired.

If the mouse was moved within the two clicks, then a normal EMIE_LMOUSE_PRESSED_DOWN will be fired instead of the EMIE_LMOUSE_DOUBLE_CLICKED at the second click, even if it was within the speed. This means the mouse has to stand still while double-clicking (just like in windows).

Default speed is 300 (milliseconds). This means every two clicks within 300 milliseconds will fire a EMIE_LMOUSE_DOUBLE_CLICKED. The speed can be adjusted with CGUIEnvironment::setDoubleClickSpeed(u32 speed).

Next step will be to integrate this into some GUI elements. For example have a listbox fire a EGET_LISTBOX_DOUBLE_CLICKED when it gets double-clicked. Also usefull would be if a textbox's text gets all selected with a doubleclick. etc.

The Linux part should work but is not tested. I would appreciate if someone could test it for me! A goot way to quickly test it is to cach the EMIE_LMOUSE_DOUBLE_CLICKED in the user event receiver and print some message to the console.

The following files have to be modified:

include/IEventReceiver.h
include/IGUIEnvironment.h
CGUIEnvironment.h
CGUIEnvironment.cpp
CIrrDeviceWin32.cpp
CIrrDeviceLinux.cpp

Here are the code changes. Lines starting with "+" have to be added and lines starting with "-" removed.

include/IEventReceiver.h

Code: Select all

	enum EMOUSE_INPUT_EVENT
	{
		.
		.
		.
		EMIE_MOUSE_WHEEL,

+		//! Left mouse button was double-clicked.
+		EMIE_LMOUSE_DOUBLE_CLICKED
	}
include/IGUIEnvironment.h

Code: Select all

public:

+	//! Used internaly by the IrrDevices to check if a double-click has occured.
+	virtual bool isDoubleClick(const SEvent& event, u32 time) = 0;
+
+	//! Sets the double-click speed in milliseconds.
+	//! Every two left mouse button clicks within this time will fire
+	//! a EMIE_LMOUSE_DOUBLE_CLICKED event (if the mouse has not moved
+	//! within the two clicks).
+	virtual void setDoubleClickSpeed(u32 speed) = 0;

CGUIEnvironment.h

Code: Select all

public:

+	//! Used internaly by the IrrDevices to check if a double-click has occured.
+	virtual bool isDoubleClick(const SEvent& event, u32 time);
+
+	//! Sets the double-click speed in milliseconds.
+	virtual void setDoubleClickSpeed(u32 speed);

Code: Select all

private:

+	struct SDoubleClick {
+		SDoubleClick():Speed(300),Time(0),X(0),Y(0){};
+		u32 Speed; // speed in milliseconds
+		u32 Time;  // time of last click
+		s32 X, Y;  // mouse coordinates of last click
+	};
+	SDoubleClick DoubleClick;

CGUIEnvironment.cpp

Code: Select all

+	//! Used internaly by the IrrDevices to check if a double-click has occured.
+	bool CGUIEnvironment::isDoubleClick(const SEvent& event, u32 time)
+	{
+		bool result = false;
+		if (time - DoubleClick.Time < DoubleClick.Speed
+			&& event.MouseInput.X == DoubleClick.X
+			&& event.MouseInput.Y == DoubleClick.Y)
+		{
+			result = true;
+			DoubleClick.Time = 0; // reset
+		} else {
+			DoubleClick.Time = time;
+		}
+
+		DoubleClick.X = event.MouseInput.X;
+		DoubleClick.Y = event.MouseInput.Y;
+	
+		return result;
+	}
+
+	//! Sets the double-click speed in milliseconds.
+	void CGUIEnvironment::setDoubleClickSpeed(u32 speed) {
+		DoubleClick.Speed = speed;
+	}

CIrrDeviceWin32.cpp

The patch for this file has been updated! Please use the code that you find 3 posts later!

Code: Select all


	case WM_MBUTTONDOWN:
		event.EventType = irr::EET_MOUSE_INPUT_EVENT;
-		event.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN;
		event.MouseInput.X = LOWORD(lParam);
		event.MouseInput.Y = HIWORD(lParam);
		dev = getDeviceFromHWnd(hWnd);
-		if (dev)
-			dev->postEventFromUser(event);
+		if (dev) {
+			if (dev->getGUIEnvironment()->isDoubleClick(event, dev->getTimer()->getTime()))
+				event.MouseInput.Event = irr::EMIE_LMOUSE_DOUBLE_CLICKED;
+			else
+				event.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN;
+			dev->postEventFromUser(event);
+		}

		return 0;

CIrrDeviceLinux.cpp

The patch for this file has been updated! Please use the code that you find 3 posts later!

Code: Select all


	case  Button5: 
		irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL; 
		irrevent.MouseInput.Wheel = -1.0f;
		break;
	}

+	if (irrevent.MouseInput.Event == irr::EMIE_LMOUSE_PRESSED_DOWN
+		&& getGUIEnvironment()->isDoubleClick(event, getTimer()->getTime()))
+			irrevent.MouseInput.Event = irr::EMIE_LMOUSE_DOUBLE_CLICKED;

	if (irrevent.MouseInput.Event != irr::EMIE_MOUSE_MOVED)
		postEventFromUser(irrevent);
	break;

Last edited by jox on Sun Jul 04, 2004 5:17 pm, edited 2 times in total.
bal
Posts: 829
Joined: Fri Jun 18, 2004 5:19 pm
Location: Geluwe, Belgium

Post by bal »

Nice work!
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

Mmh, I have a little issue...

I think it would be better to always fire a EMIE_LMOUSE_PRESSED_DOWN, even if a double-click happend. This means in case of a double-click two events will be fired: EMIE_LMOUSE_PRESSED_DOWN and EMIE_LMOUSE_DOUBLE_CLICKED.

The reason is because of the following:

If for instance a gui element does not care about double-clicks, but does care about fast repetetive clicks with the left mouse button (e.g. +/- buttons to increment/decrement a value or the buttons of the scrollbar) then every second click would get lost because a double-click would have been detected. (You could have the button recognize double-clicks as normal clicks but that's not such a good idea...)

I will update the code later and post it then!
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

Here is the update. CIrrDeviceWin32.cpp and CIrrDeviceLinux.cpp have different changes now. The changes in the other files stay the same:

CIrrDeviceWin32.cpp

Code: Select all

	case WM_MBUTTONDOWN:
		event.EventType = irr::EET_MOUSE_INPUT_EVENT;
		event.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN;
		event.MouseInput.X = LOWORD(lParam);
		event.MouseInput.Y = HIWORD(lParam);
		dev = getDeviceFromHWnd(hWnd);
-		if (dev)
-			dev->postEventFromUser(event);
+		if (dev)
+		{
+			dev->postEventFromUser(event);
+			if (dev->getGUIEnvironment()->isDoubleClick(event, dev->getTimer()->getTime()))
+			{
+				event.MouseInput.Event = irr::EMIE_LMOUSE_DOUBLE_CLICKED;
+				dev->postEventFromUser(event);
+			}
+		}
		return 0;

CIrrDeviceLinux.cpp

Code: Select all

	case  Button5: 
		irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL; 
		irrevent.MouseInput.Wheel = -1.0f;
		break;
	}

-	if (irrevent.MouseInput.Event != irr::EMIE_MOUSE_MOVED)
-		postEventFromUser(irrevent);
+	if (irrevent.MouseInput.Event != irr::EMIE_MOUSE_MOVED)
+	{
+		postEventFromUser(irrevent);
+		if (irrevent.MouseInput.Event == irr::EMIE_LMOUSE_PRESSED_DOWN
+			&& getGUIEnvironment()->isDoubleClick(irrevent, getTimer()->getTime()))
+		{
+			irrevent.MouseInput.Event = irr::EMIE_LMOUSE_DOUBLE_CLICKED;
+			postEventFromUser(irrevent);
+		}
+	}
	break;
Edit: 'event' changed to 'irrevent' (see next post)
Last edited by jox on Fri Aug 20, 2004 12:22 pm, edited 1 time in total.
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

magic from IrlichtNX found a little thing:

&& getGUIEnvironment()->isDoubleClick(event, getTimer()->getTime()))
has to be
&& getGUIEnvironment()->isDoubleClick(irrevent, getTimer()->getTime()))

thanx to him.
Post Reply