changeset 133:9fd705793568

Fixed menu popup bug, improved recursion detection. Menu popups can now determine whether or not they are sub-menus. Recursion detection can now also check content (if not the same, there's not a risk of infinite recursion).
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 23 Jan 2009 16:05:05 +0000
parents 264028f4115a
children 7ababdf97748
files data/conf/guiDemo.mtt mde/gui/WidgetManager.d mde/gui/exception.d mde/gui/widget/AParentWidget.d mde/gui/widget/Ifaces.d mde/gui/widget/PopupMenu.d mde/gui/widget/layout.d mde/menus.d
diffstat 8 files changed, 59 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/data/conf/guiDemo.mtt	Fri Jan 23 14:59:05 2009 +0000
+++ b/data/conf/guiDemo.mtt	Fri Jan 23 16:05:05 2009 +0000
@@ -8,12 +8,9 @@
 <WidgetData|blank={0:[0x2]}>
 
 <WidgetData|menuContent={0:[0x2031],1:["imde.menus","menus"]}>
-<WidgetData|menus={0:[0xC110,8]    ,1:["menuPopup"]}>
+<WidgetData|menus={0:[0x4110,12]   ,1:["menuPopup"]}>
 <WidgetData|menuPopup={0:[0x6033,0],1:["menuList"]}>
-!{Could recurse to menuPopup if it wasn't for recursion protection:}
-<WidgetData|menuList={0:[0xE030,0] ,1:["menu1Popup"]}>
-<WidgetData|menu1Popup={0:[0x6033] ,1:["menu1List"]}>
-<WidgetData|menu1List={0:[0xE030,0],1:["blank"]}>
+<WidgetData|menuList={0:[0x6030,0] ,1:["menuPopup"]}>
 
 <EnumContent|switch={0:["misc","video","font"],1:0}>
 <WidgetData|options={0:[0x2031],1:["dynamic.switch","switchL"]}>
@@ -26,12 +23,12 @@
 <WidgetData|optFont={0:[0x2031],1:["Options.FontOptions","optSec"]}>
 
 !{use optBox for no description, optDBox for descriptions under entries}
-<WidgetData|optSec={0:[0xC110,0],1:["optBox"]}>
+<WidgetData|optSec={0:[0x4110,0],1:["optBox"]}>
 <WidgetData|optDBox={0:[0x4100,1,2,1],1:["optBox","optDesc"]}>
 <WidgetData|optBox={0:[0x4100,1,1,3],1:["optName","optSep","optVal"]}>
 <WidgetData|optName={0:[0x4020, 1, 0xffffff]}>
 <WidgetData|optDesc={0:[0x4020, 2, 0x999999]}>
-<WidgetData|optVal={0:[0xE030,12],1:["optEnum"]}>
+<WidgetData|optVal={0:[0x6030,12],1:["optEnum"]}>
 <WidgetData|optEnum={0:[0x4100,0,1,2],1:["optVal","optName"]}>
 <WidgetData|optSep={0:[0x21, 0xff],1:[" = "]}>
 {Basic}
--- a/mde/gui/WidgetManager.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/gui/WidgetManager.d	Fri Jan 23 16:05:05 2009 +0000
@@ -268,7 +268,7 @@
     //BEGIN IParentWidget methods
     // If call reaches the widget manager there isn't any recursion.
     //NOTE: should be override
-    final void recursionCheck (widgetID) {}
+    final void recursionCheck (widgetID, IContent) {}
     
     override void minWChange (IChildWidget widget, wdim nmw) {
         debug assert (widget is child, "WM.mSC (code error)");
@@ -320,6 +320,9 @@
     override bool menuActive () {
         return mAIPPW;
     }
+    override bool parentMenuActive () {
+        return false;
+    }
     
     // Don't do anything. E.g. can get called by non-popup buttons.
     override void menuDone () {}
@@ -473,7 +476,6 @@
 enum WIDGET_TYPE : int {
     FUNCTION		= 0x2000,   // Function called instead of widget created (no "Widget" appended to fct name)
     TAKES_CONTENT	= 0x4000,   // Flag indicates widget's this should be passed an IContent reference.
-    SAFE_RECURSION	= 0x8000,   // Safe to instantiate recursively without infinite looping.
     
     // Use widget names rather than usual capitals convention
     Unnamed		= 0x0,      // Only for use by widgets not created with createWidget
@@ -491,7 +493,7 @@
     TextLabel		= 0x21,
     
     // content functions: 0x30
-    editContent		= FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x30,
+    editContent		= FUNCTION | TAKES_CONTENT | 0x30,
     addContent		= FUNCTION | 0x31,
     popupListContent	= FUNCTION | TAKES_CONTENT | 0x33,
     
@@ -502,7 +504,7 @@
     ButtonContent	= TAKES_CONTENT | 0x43,
     
     GridLayout		= TAKES_CONTENT | 0x100,
-    ContentList		= TAKES_CONTENT | SAFE_RECURSION | 0x110,
+    ContentList		= TAKES_CONTENT | 0x110,
     
     FloatingArea	= TAKES_CONTENT | 0x200,
     Switch		= TAKES_CONTENT | 0x210,
@@ -522,11 +524,11 @@
 	"AStringContent",
 	"ButtonContent",
 	"GridLayout",
+	"ContentList",
 	"FloatingArea",
 	"Switch",
-	"popupListContent",
-	"ContentList",
-	"editContent"];
+	"editContent",
+	"popupListContent"];
 
 /* Generates a binary search algorithm for makeWidget. */
 char[] binarySearch (char[] var, char[][] consts) {
@@ -541,8 +543,7 @@
         foreach (c; consts) {
             ret ~= `if (` ~ var ~ ` == WIDGET_TYPE.` ~ c ~ `) {
                         debug (mdeWidgets) logger.trace ("Creating new `~c~`.");
-                        if (!(WIDGET_TYPE.`~c~` & WIDGET_TYPE.SAFE_RECURSION))
-                    	  parent.recursionCheck (id);
+                        parent.recursionCheck (id, content);
                         static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.FUNCTION)
                           return `~c~` (this, parent, id, data, content);
                         else static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.TAKES_CONTENT)
--- a/mde/gui/exception.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/gui/exception.d	Fri Jan 23 16:05:05 2009 +0000
@@ -40,6 +40,14 @@
     }
 }
 
+/// Thrown when a widget is (potentially) being recursed infinitely.
+class WidgetRecursionException : GuiException
+{
+    this (char[] id) {	// Pass id of widget being recursed
+        super ("Infinite recursion of "~id);
+    }
+}
+
 class ContentException : GuiException
 {
     char[] getSymbol () {
--- a/mde/gui/widget/AParentWidget.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/gui/widget/AParentWidget.d	Fri Jan 23 16:05:05 2009 +0000
@@ -23,6 +23,7 @@
 
 public import mde.gui.widget.AChildWidget;
 import mde.gui.exception;
+import mde.content.Content;
 
 debug {
     import tango.util.log.Log : Log, Logger;
@@ -72,13 +73,13 @@
         throw new GuiException ("getWidgetIndex: widget not found (code error)");
     }
     
-    // Don't override; use the WIDGET_TYPE.SAFE_RECURSION flag for safe widgets.
-    //NOTE: should be override (compiler bug)
-    final void recursionCheck (widgetID a) {
+    // Parents taking a content should override, only throwing if both the
+    // widget id and the content are the same (as its it and content).
+    override void recursionCheck (widgetID wID, IContent c) {
         debug assert (id !is null && parent !is null, "recursionCheck called before parent and id set");
-        if (a is id)
-            throw new GuiException ("Infite recursion of "~a);
-        parent.recursionCheck (a);
+        if (wID is id)
+            throw new WidgetRecursionException (wID);
+        parent.recursionCheck (wID, c);
     }
     
     IPopupParentWidget getParentIPPW () {
@@ -151,7 +152,10 @@
     override bool menuActive () {
         return mAIPPW;
     }
-    
+    override bool parentMenuActive () {
+        return parentIPPW.menuActive;
+    }
+   
     override void menuDone () {	// default actions, for popup menus:
         parentIPPW.removeChildIPPW (this);	// remove self
         parentIPPW.menuDone;			// and propegate
--- a/mde/gui/widget/Ifaces.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/gui/widget/Ifaces.d	Fri Jan 23 16:05:05 2009 +0000
@@ -47,7 +47,7 @@
 interface IParentWidget
 {
     /** Checks for recursion of unsafe widgets to prevent infinite recursion. */
-    void recursionCheck (widgetID);
+    void recursionCheck (widgetID, IContent);
     
     /** IPPWs return self, other widgets recurse call on parent. */
     IPopupParentWidget getParentIPPW ();
@@ -132,6 +132,10 @@
      * over and buttons activated with an up-click. */
     void menuActive (bool);
     bool menuActive ();
+    /** Returns the IPPW's parent's menuActive (WM returns false). If true,
+     * popup widgets may assume they are sub-menu popups not top-level menu
+     * popups. */
+    bool parentMenuActive ();
     
     /** Called by descendant widgets such as buttons when an action occurred,
      * which should close a menu. (But also called when not in a menu.) */
--- a/mde/gui/widget/PopupMenu.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/gui/widget/PopupMenu.d	Fri Jan 23 16:05:05 2009 +0000
@@ -51,6 +51,12 @@
 	h = mh;
     }
     
+    override void recursionCheck (widgetID wID, IContent c) {
+        if (wID is id && c is content)
+            throw new WidgetRecursionException (wID);
+        parent.recursionCheck (wID, c);
+    }
+
     override int clickEvent (wdabs, wdabs, ubyte b, bool state) {
 	if (b == 1 && state == true) {
 	    if (!pushed) {
@@ -58,10 +64,7 @@
                 parentIPPW.menuActive = true;
                 mgr.positionPopup (this, popup);
                 pushed = true;
-            } else {
-                // NOTE: perhaps shouldn't do anything when
-                // parentIPPW.parentIPPW.menuActive
-                // (this causes funny behaviour when clicking a submenu):
+            } else if (!parentIPPW.parentMenuActive) {	// if not a submenu
                 parentIPPW.removeChildIPPW (this);
             }
 	}
@@ -77,7 +80,8 @@
         if (state && !pushed && parentIPPW.menuActive) {
             parentIPPW.addChildIPPW (this);
             menuActive = true;
-            mgr.positionPopup (this, popup, 1);	// causes redraw
+            mgr.positionPopup (this, popup,
+                               parentIPPW.parentMenuActive ? 1 : 0);
             pushed = true;
         }
     }
--- a/mde/gui/widget/layout.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/gui/widget/layout.d	Fri Jan 23 16:05:05 2009 +0000
@@ -116,6 +116,12 @@
         }
     }
     
+    override void recursionCheck (widgetID wID, IContent c) {
+        if (wID is id && c is cList)
+            throw new WidgetRecursionException (wID);
+        parent.recursionCheck (wID, c);
+    }
+    
     override bool saveChanges () {
         // Since all sub-widgets have the same id, it only makes sense to call on one
         if (subWidgets is null)
--- a/mde/menus.d	Fri Jan 23 14:59:05 2009 +0000
+++ b/mde/menus.d	Fri Jan 23 16:05:05 2009 +0000
@@ -36,5 +36,10 @@
         debug logger.trace ("Quit (from menu)");
         run = false;
     });
-    menus.append (new ContentList ("main", [cast(Content)quit]));
+    auto main = new ContentList ("main", [cast(Content)quit]);
+    debug {
+        main.append (new ContentList ("sm1", [cast(Content) new EventContent ("a"), new EventContent ("b")]));
+        main.append (new ContentList ("sm2", [cast(Content) new EventContent ("c"), new EventContent ("d")]));
+    }
+    menus.append (main);
 }