GUI Modification: Leftward Cascading SubMenu

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
Narcan
Posts: 3
Joined: Wed May 27, 2009 2:55 pm

GUI Modification: Leftward Cascading SubMenu

Post by Narcan »

One of my projects uses sub menus near the right edge of the screen. Since the default Irrlicht sub menu only cascades to the right (off the screen in this case), I added the option of cascading to the left.

This version of the addItem() function is overloaded to accept a bool that determines if the menu expands normally or is reversed.

Normal:

Code: Select all

submenu->addItem(L"About", 500, true, true, false);
Image

Reversed:

Code: Select all

submenu->addItem(L"About", 500, true, true, false, true);
Image

Diff:

Code: Select all

CGUIContextMenu.cpp:

71a72
> 	s.ReverseCascade = false;
85a87,109
> u32 CGUIContextMenu::addItem(const wchar_t* text, s32 id, bool enabled, bool hasSubMenu, bool checked, bool cascadeLeft)
> {
> 	SItem s;
> 	s.Enabled = enabled;
> 	s.Checked = checked;
> 	s.Text = text;
> 	s.IsSeparator = (text == 0);
> 	s.SubMenu = 0;
> 	s.CommandId = id;
> 	s.ReverseCascade = cascadeLeft;
> 
> 	if (hasSubMenu)
> 	{
> 		s.SubMenu = new CGUIContextMenu(Environment, this, id,
> 			core::rect<s32>(0,0,100,100), false, false);
> 		s.SubMenu->setVisible(false);
> 	}
> 
> 	Items.push_back(s);
> 
> 	recalculateSize();
> 	return Items.size() - 1;
> }
502a527,529
> 
> 				if (!Items[i].ReverseCascade)
> 				{
511a539,550
> 				else
> 				{
> 					r.LowerRightCorner.X = r.UpperLeftCorner.X - 15;
> 
> 					sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_LEFT),
> 						r.getCenter(), clip, skin->getColor(c),
> 						(i == HighLighted) ? ChangeTime : 0,
> 						(i == HighLighted) ? os::Timer::getTime() : 0,
> 						(i == HighLighted), true);
> 				}
> 			}
> 
583a623,624
> 			if (!Items[i].ReverseCascade)
> 			{
586a628,635
> 			}
> 
> 			else
> 			{
> 				Items[i].SubMenu->setRelativePosition(
> 					core::rect<s32>(0 - w, Items[i].PosY,
> 						0, Items[i].PosY+h));
> 			}

CGUIContextMenu.h:

40a41,43
> 		virtual u32 addItem(const wchar_t* text, s32 commandid,
> 				bool enabled, bool hasSubMenu, bool checked, bool cascadeLeft);
> 
109a113
> 			bool ReverseCascade;

IGUIContextMenu.h:

38,41c38,60
< 		virtual u32 addItem(const wchar_t* text, s32 commandId=-1, bool enabled=true,
< 			bool hasSubMenu=false,
< 			bool checked=false
< 			) = 0;
---
> 
> 		virtual u32 addItem(const wchar_t* text, s32 commandid,
> 				bool enabled, bool hasSubMenu, bool checked)
> 		{
> 			commandid=-1;
> 			enabled=true;
> 			hasSubMenu=false;
> 			checked=false;
> 
> 			return 0;
> 		};
> 
> 		virtual u32 addItem(const wchar_t* text, s32 commandid,
> 				bool enabled, bool hasSubMenu, bool checked, bool cascadeLeft)
> 		{
> 			commandid=-1;
> 			enabled=true;
> 			hasSubMenu=false;
> 			checked=false;
> 			cascadeLeft=false;
> 
> 			return 0;
> 		};

Sylence
Posts: 725
Joined: Sat Mar 03, 2007 9:01 pm
Location: Germany
Contact:

Post by Sylence »

Nice!
But imho it would be better to let the code decide if it should cascade left or right by checking if the right menu would hang off the screen.
Software documentation is like sex. If it's good you want more. If it's bad it's better than nothing.
Narcan
Posts: 3
Joined: Wed May 27, 2009 2:55 pm

Post by Narcan »

I considered doing it that way, since it would probably be useful to more people. For my purpose, though, it's best to outright dictate which way the menus go.

Some of my menus are popping out of a vertical column along the right edge of the screen. I know ahead of time that all those menus should only go left, so there's no reason to calculate it on the fly. (I also wanted to avoid the circumstance where they start to expand right, cross the column of buttons, hit the edge and zigzag back. Which meant precalculating the width of every submenu or just using a bool.)

I may write another version that does it your way if I get bored or there's enough interest. :)

Thanks for the feedback.
MasterGod
Posts: 2061
Joined: Fri May 25, 2007 8:06 pm
Location: Israel
Contact:

Post by MasterGod »

Cool.

By the way, I think it would be better posting the patch instead of the diff.
Image
Dev State: Abandoned (For now..)
Requirements Analysis Doc: ~87%
UML: ~0.5%
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

Nice one, thanks for posting.
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
Narcan
Posts: 3
Joined: Wed May 27, 2009 2:55 pm

Post by Narcan »

I just switched from 1.5 to SVN. Here's the patch for revision 2410 if anyone is interested:

Code: Select all

Index: include/IGUIContextMenu.h
===================================================================
--- include/IGUIContextMenu.h	(revision 2410)
+++ include/IGUIContextMenu.h	(working copy)
@@ -35,9 +35,30 @@
 		at this item. You can acess this submenu via getSubMenu().
 		\param checked: Specifies if the menu item should be initially checked.
 		\return Returns the index of the new item */
-		virtual u32 addItem(const wchar_t* text, s32 commandId=-1, bool enabled=true,
-			bool hasSubMenu=false, bool checked=false) = 0;
 
+		virtual u32 addItem(const wchar_t* text, s32 commandid,
+			bool enabled, bool hasSubMenu, bool checked)
+			{
+				commandid=-1;
+				enabled=true;
+				hasSubMenu=false;
+				checked=false;
+
+				return 0;
+			}; 
+
+		virtual u32 addItem(const wchar_t* text, s32 commandid,
+			bool enabled, bool hasSubMenu, bool checked, bool cascadeLeft)
+			{
+				commandid=-1;
+				enabled=true;
+				hasSubMenu=false;
+				checked=false;
+				cascadeLeft=false;
+
+				return 0;
+			}; 
+
 		//! Adds a separator item to the menu
 		virtual void addSeparator() = 0;
 
Index: source/Irrlicht/CGUIContextMenu.cpp
===================================================================
--- source/Irrlicht/CGUIContextMenu.cpp	(revision 2410)
+++ source/Irrlicht/CGUIContextMenu.cpp	(working copy)
@@ -69,6 +69,7 @@
 	s.IsSeparator = (text == 0);
 	s.SubMenu = 0;
 	s.CommandId = id;
+	s.ReverseCascade = false;
 
 	if (hasSubMenu)
 	{
@@ -83,7 +84,30 @@
 	return Items.size() - 1;
 }
 
+u32 CGUIContextMenu::addItem(const wchar_t* text, s32 id, bool enabled, bool hasSubMenu, bool checked, bool cascadeLeft)
+{
+	SItem s;
+	s.Enabled = enabled;
+	s.Checked = checked;
+	s.Text = text;
+	s.IsSeparator = (text == 0);
+	s.SubMenu = 0;
+	s.CommandId = id;
+	s.ReverseCascade = cascadeLeft;
 
+	if (hasSubMenu)
+	{
+		s.SubMenu = new CGUIContextMenu(Environment, this, id,
+			core::rect<s32>(0,0,100,100), false, false);
+		s.SubMenu->setVisible(false);
+	}
+
+	Items.push_back(s);
+
+	recalculateSize();
+	return Items.size() - 1;
+}
+
 //! Adds a sub menu from an element that already exists.
 void CGUIContextMenu::setSubMenu(u32 index, CGUIContextMenu* menu)
 {
@@ -500,13 +524,28 @@
 			if (Items[i].SubMenu && sprites)
 			{
 				core::rect<s32> r = rect;
-				r.UpperLeftCorner.X = r.LowerRightCorner.X - 15;
 
-				sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_RIGHT),
-					r.getCenter(), clip, skin->getColor(c),
-					(i == HighLighted) ? ChangeTime : 0,
-					(i == HighLighted) ? os::Timer::getTime() : 0,
-					(i == HighLighted), true);
+				if (!Items[i].ReverseCascade)
+				{
+					r.UpperLeftCorner.X = r.LowerRightCorner.X - 15;
+
+					sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_RIGHT),
+						r.getCenter(), clip, skin->getColor(c),
+						(i == HighLighted) ? ChangeTime : 0,
+						(i == HighLighted) ? os::Timer::getTime() : 0,
+						(i == HighLighted), true);
+				}
+
+				else
+				{
+					r.LowerRightCorner.X = r.UpperLeftCorner.X - 15;
+
+					sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_LEFT),
+						r.getCenter(), clip, skin->getColor(c),
+						(i == HighLighted) ? ChangeTime : 0,
+						(i == HighLighted) ? os::Timer::getTime() : 0,
+						(i == HighLighted), true);
+				}
 			}
 
 			// draw checked symbol
@@ -580,9 +619,19 @@
 			const s32 w = Items[i].SubMenu->getAbsolutePosition().getWidth();
 			const s32 h = Items[i].SubMenu->getAbsolutePosition().getHeight();
 
-			Items[i].SubMenu->setRelativePosition(
-				core::rect<s32>(width-5, Items[i].PosY,
-					width+w-5, Items[i].PosY+h));
+			if (!Items[i].ReverseCascade)
+			{
+				Items[i].SubMenu->setRelativePosition(
+					core::rect<s32>(width-5, Items[i].PosY,
+						width+w-5, Items[i].PosY+h));
+			}
+
+			else
+			{
+				Items[i].SubMenu->setRelativePosition(
+					core::rect<s32>(0 - w, Items[i].PosY,
+						0, Items[i].PosY+h));
+			}
 		}
 	}
 }
Index: source/Irrlicht/CGUIContextMenu.h
===================================================================
--- source/Irrlicht/CGUIContextMenu.h	(revision 2410)
+++ source/Irrlicht/CGUIContextMenu.h	(working copy)
@@ -38,6 +38,9 @@
 		virtual u32 addItem(const wchar_t* text, s32 commandid,
 				bool enabled, bool hasSubMenu, bool checked);
 
+		virtual u32 addItem(const wchar_t* text, s32 commandid,
+				bool enabled, bool hasSubMenu, bool checked, bool cascadeLeft);
+
 		//! Adds a separator item to the menu
 		virtual void addSeparator();
 
@@ -111,6 +114,7 @@
 			s32 PosY;
 			CGUIContextMenu* SubMenu;
 			s32 CommandId;
+			bool ReverseCascade;
 		};
 
 		virtual void recalculateSize();
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

I consider this a bug, added to the tracker. This patch will need a rewrite before inclusion though, as it should be automatic.
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

Hm, I think I have an automatic patch at least for the sub-menues. I just didn't include it so far because I have another 3 menu patches on my list for which I haven't found time yet.

My patch is rather simple - all I do is before positioning the submenues to check if they fit into the root element rectangle and when they don't I move them to the left.

Code: Select all

diff -r 22573a580586 lib/irrlicht/source/Irrlicht/CGUIContextMenu.cpp
--- a/lib/irrlicht/source/Irrlicht/CGUIContextMenu.cpp	Sat Jul 04 01:26:03 2009 +0200
+++ b/lib/irrlicht/source/Irrlicht/CGUIContextMenu.cpp	Thu Jul 09 09:51:01 2009 +0200
@@ -671,9 +671,21 @@
 			const s32 w = Items[i].SubMenu->getAbsolutePosition().getWidth();
 			const s32 h = Items[i].SubMenu->getAbsolutePosition().getHeight();
 
-			Items[i].SubMenu->setRelativePosition(
-				core::rect<s32>(width-5, Items[i].PosY,
-					width+w-5, Items[i].PosY+h));
+            core::rect<s32> subRect(width-5, Items[i].PosY, width+w-5, Items[i].PosY+h);
+
+            // if it would be drawn beyond the right border, then add it to the left side
+            gui::IGUIElement * root = Environment->getRootGUIElement();
+            if ( root )
+            {
+                core::rect<s32> rectRoot( root->getAbsolutePosition() );
+                if ( getAbsolutePosition().UpperLeftCorner.X+subRect.LowerRightCorner.X > rectRoot.LowerRightCorner.X )
+                {
+                    subRect.UpperLeftCorner.X = -w;
+                    subRect.LowerRightCorner.X = 0;
+                }
+            }
+
+			Items[i].SubMenu->setRelativePosition(subRect);
 		}
 	}
 }
Would that sound ok?
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