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.

GUI Modification: Leftward Cascading SubMenu

Postby Narcan » Wed May 27, 2009 3:45 pm

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;
>       };

Narcan
 
Posts: 3
Joined: Wed May 27, 2009 2:55 pm

Postby Sylence » Wed May 27, 2009 5:22 pm

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.
User avatar
Sylence
 
Posts: 725
Joined: Sat Mar 03, 2007 9:01 pm
Location: Germany

Postby Narcan » Wed May 27, 2009 5:58 pm

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

Postby MasterGod » Thu May 28, 2009 9:53 pm

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%
User avatar
MasterGod
 
Posts: 2061
Joined: Fri May 25, 2007 8:06 pm
Location: Israel

Postby CuteAlien » Fri May 29, 2009 12:01 am

Nice one, thanks for posting.
IRC: #irrlicht on irc.freenode.net
My patches&stuff: http://www.michaelzeilfelder.de/irrlicht.htm
Games with Irrlicht: http://www.irrgheist.com/
News: http://www.reddit.com/r/irrlicht/
User avatar
CuteAlien
Admin
 
Posts: 5349
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany

Postby Narcan » Fri Jun 05, 2009 6:59 pm

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

Postby bitplane » Thu Aug 20, 2009 6:24 pm

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
User avatar
bitplane
Admin
 
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England

Postby CuteAlien » Thu Aug 20, 2009 7:10 pm

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.freenode.net
My patches&stuff: http://www.michaelzeilfelder.de/irrlicht.htm
Games with Irrlicht: http://www.irrgheist.com/
News: http://www.reddit.com/r/irrlicht/
User avatar
CuteAlien
Admin
 
Posts: 5349
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany


Return to Bug reports

Who is online

Users browsing this forum: No registered users and 0 guests