diff mde/gui/WidgetManager.d @ 131:9cff74f68b84

Major revisions to popup handling. Buttons can close menus now, plus some smaller impovements. Removed Widget module. Moved Widget.AWidget to AChildWidget.AChildWidget and Widget.AParentWidget to AParentWidget.AParentWidget. Removed ASingleParentWidget to improve code sharing. AChildWidget doesn't implement IParentWidget like AWidget did. New IPopupParentWidget extending IParentWidget for the WM and some widgets to handle popups. Cut old popup management code. New underMouse() function replacing highlight(); called on all widgets. Separate menu-popup and button widgets aren't needed for menus now. Functions returning content widgets have been moved to their own module. Cleaned up jobs.txt. Switched to 80 line length for Ddoc.
author Diggory Hardy <diggory.hardy@gmail.com>
date Wed, 21 Jan 2009 13:01:40 +0000
parents ad91de8867a0
children 264028f4115a
line wrap: on
line diff
--- a/mde/gui/WidgetManager.d	Sat Jan 17 16:11:26 2009 +0000
+++ b/mde/gui/WidgetManager.d	Wed Jan 21 13:01:40 2009 +0000
@@ -13,12 +13,15 @@
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-/*************************************************************************************************
+/******************************************************************************
  * The gui manager class base.
  *
- * This contains most of the code required by a window manager, but does not interact with a screen
- * or get user input. Rendering is handled separately by the renderer anyway.
- *************************************************************************************************/
+ * This contains most of the code required by a window manager, but does not
+ * interact with a screen or get user input. Rendering is handled separately by
+ * the renderer anyway.
+ * 
+ * Public non IWidget* methods should be thread-safe.
+ *****************************************************************************/
 module mde.gui.WidgetManager;
 
 import mde.gui.WidgetDataSet;
@@ -39,6 +42,7 @@
 import mde.gui.widget.layout;
 import mde.gui.widget.miscWidgets;
 import mde.gui.widget.TextWidget;
+import mde.gui.widget.contentFunctions;
 import mde.gui.widget.miscContent;
 import mde.gui.widget.Floating;
 import mde.gui.widget.PopupMenu;
@@ -52,12 +56,12 @@
     logger = Log.getLogger ("mde.gui.WidgetManager");
 }
 
-/*************************************************************************************************
- * Contains the code for loading and saving an entire gui (more than one may exist), but not the
- * code for drawing it or handling user input.
+/******************************************************************************
+ * Contains the code for loading and saving an entire gui (more than one may
+ * exist), but not the code for drawing it or handling user input.
  * 
  * This abstract class exists solely for separating out some of the functionality.
- *************************************************************************************************/
+ *****************************************************************************/
 abstract scope class AWidgetManager : IWidgetManager
 {
     /** Construct a new widget loader.
@@ -185,6 +189,7 @@
         
         // Create the widgets:
         createRootWidget;
+        underMouse = child;	// must be something
     }
     
     /** Save changes, if any exist.
@@ -247,7 +252,7 @@
     }
     
     /** Called when translation strings have been reloaded. */
-    void reloadStrings (Content) {
+    protected void reloadStrings (Content) {
 	Items.loadTranslation;
 	child.setup (++setupN, 2);
 	child.setWidth  (w, -1);
@@ -256,6 +261,9 @@
 	requestRedraw;
     }
     
+    // These methods are only intended for use within the gui package.
+    // They are not necessarily thread-safe:
+    
     //BEGIN IParentWidget methods
     // If call reaches the widget manager there isn't any recursion.
     //NOTE: should be override
@@ -283,6 +291,58 @@
     }
     //END IParentWidget methods
     
+    //BEGIN IPopupParentWidget methods
+    override IPopupParentWidget getParentIPPW () {
+        return this;
+    }
+    
+    override void addChildIPPW (IPopupParentWidget ippw) {
+        if (childIPPW)
+            childIPPW.removedIPPW;
+        childIPPW = ippw;
+        requestRedraw;
+    }
+    override bool removeChildIPPW (IPopupParentWidget ippw) {
+        if (childIPPW !is ippw) return false;
+        childIPPW.removedIPPW;
+        childIPPW = null;
+        mAIPPW = false;
+        requestRedraw;
+        return false;
+    }
+    
+    override void menuActive (bool mA) {
+        mAIPPW = mA;
+        if (childIPPW)
+            childIPPW.menuActive = mA;
+    }
+    override bool menuActive () {
+        return mAIPPW;
+    }
+    
+    // Don't do anything. E.g. can get called by non-popup buttons.
+    override void menuDone () {}
+    
+    override IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup) {
+        IChildWidget ret;
+        if (childIPPW) {
+            ret = childIPPW.getPopupWidget (cx, cy, closePopup);
+            if (closePopup && ret is null) {
+                menuActive = false;
+                removeChildIPPW (childIPPW);
+            }
+        }
+        return ret;
+    }
+    
+    debug protected override bool isChild (IPopupParentWidget ippw) {
+        return ippw is childIPPW;
+    }
+    
+    override void removedIPPW () {}	// irrelevant
+    override void drawPopup () {}
+    //END IPopupParentWidget methods
+    
     //BEGIN IWidgetManager methods
     override IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null)
     {
@@ -325,44 +385,28 @@
         changes.setDims(id, d);		// also updates WidgetDataSet in data.
     }
     
-    // These methods are only intended for use within the gui package. They are not necessarily
-    // thread-safe.
     IRenderer renderer () {
         assert (rend !is null, "WidgetManager.renderer: rend is null");
         return rend;
     }
     
-    void addPopup (IChildWidget parnt, IChildWidget widg, int flags = 0) {
-	debug assert (parnt && widg, "addPopup: null widget");
-        if (popups.length >= popupsMem.length)
-            popupsMem.length = popupsMem.length * 2 + 2;
-        with (popupsMem[popups.length]) {
-            parent = parnt;
-            widget = widg;
-	    w = widg.width;
-	    h = widg.height;
-            if (flags & 1) {
-	    	y = parent.yPos;
-            	if (y+h > this.h) y += parent.height - h;
-	    	x = parent.xPos + parent.width;
-	    	if (x+w > this.w) x = parent.xPos - w;
-            } else {
-                x = parent.xPos;				// align on left edge
-                if (x+w > this.w) x += parent.width - w;	// align on right edge
-                y = parent.yPos + parent.height;		// place below
-                if (y+h > this.h) y = parent.yPos - h;		// place above
-            }
-	    widget.setPosition (x, y);
-	}
-        popups = popupsMem[0..popups.length+1];
-	requestRedraw;
-    }
-    void removePopup (IChildWidget parnt) {
-	foreach_reverse (i,popup; popups) {
-	    if (popup.parent is parnt)
-		popups = popups[0..i];
-	}
-	requestRedraw;
+    void positionPopup (IChildWidget parent, IChildWidget popup, int flags = 0) {
+	debug assert (parent && popup, "positionPopup: null widget");
+        wdim w = popup.width,
+             h = popup.height,
+             x, y;
+        if (flags & 1) {
+            y = parent.yPos;
+            if (y+h > this.h) y += parent.height - h;
+            x = parent.xPos + parent.width;
+            if (x+w > this.w) x = parent.xPos - w;
+        } else {
+            x = parent.xPos;				// align on left edge
+            if (x+w > this.w) x += parent.width - w;	// align on right edge
+            y = parent.yPos + parent.height;		// place below
+            if (y+h > this.h) y = parent.yPos - h;		// place above
+        }
+        popup.setPosition (x, y);
     }
 
     void requestRedraw () {
@@ -382,11 +426,25 @@
     //END IWidgetManager methods
     
     debug void logWidgetSize (Content) {
-        logger.trace ("Current size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh);
+        logger.trace ("size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh);
         child.logWidgetSize;
     }
     
 protected:
+    void updateUnderMouse (wdabs cx, wdabs cy, bool closePopup) {
+        auto oUM = underMouse;
+        underMouse = getPopupWidget (cx, cy, closePopup);
+        if (underMouse is null) {
+            debug assert (child.onSelf (cx, cy), "WidgetManager: child doesn't cover whole area");
+            underMouse = child.getWidget (cx, cy);
+        }
+        if (underMouse !is oUM) {
+            debug assert (oUM && underMouse, "no widget under mouse: error");
+            oUM.underMouse (false);
+            underMouse.underMouse (true);
+        }
+    }
+    
     /** Second stage of loading the widgets.
     * 
     * loadDesign handles the data; this method needs to:
@@ -426,7 +484,6 @@
     
     // popup widgets: 0x10
     PopupMenu		= TAKES_CONTENT | 0x11,
-    SubMenu		= TAKES_CONTENT | 0x12,
     
     // labels: 0x20
     ContentLabel	= TAKES_CONTENT | 0x20,
@@ -443,7 +500,6 @@
     BoolContent		= TAKES_CONTENT | 0x41,
     AStringContent	= TAKES_CONTENT | 0x42,
     ButtonContent	= TAKES_CONTENT | 0x43,
-    MenuButtonContent	= TAKES_CONTENT | 0x44,
     
     GridLayout		= TAKES_CONTENT | 0x100,
     ContentList		= TAKES_CONTENT | SAFE_RECURSION | 0x110,
@@ -460,13 +516,11 @@
 	"TextLabel",
 	"addContent",
 	"PopupMenu",
-	"SubMenu",
 	"ContentLabel",
         "DisplayContent",
         "BoolContent",
 	"AStringContent",
 	"ButtonContent",
-	"MenuButtonContent",
 	"GridLayout",
 	"FloatingArea",
 	"Switch",
@@ -540,19 +594,14 @@
     scope IChildWidget child;		// The primary widget.
     uint setupN;			// n to pass to IChildWidget.setup
     
-    struct ActivePopup {
-        IChildWidget widget;
-	IChildWidget parent;
-        wdabs x,y;
-        wdsize w,h;
-    }
-    ActivePopup[] popups;	// Pop-up [menus] to draw. Last element is top popup.
-    ActivePopup[] popupsMem;	// allocated memory for popups
+    bool mAIPPW;			// IPPW variable
+    IPopupParentWidget childIPPW;	// child IPPW, if any active
+    
     // callbacks indexed by their frame pointers. Must support removal of elements in foreach:
     SortedMap!(void*,bool delegate(wdabs cx, wdabs cy, ubyte b, bool state)) clickCallbacks;
     SortedMap!(void*,void delegate(wdabs cx, wdabs cy)) motionCallbacks;
-    IChildWidget keyFocus;	// widget receiving keyboard input when non-null
-    IChildWidget highlighted;	// NOTE: in some ways should be same as keyFocus
+    IChildWidget keyFocus;	// widget receiving keyboard input
+    IChildWidget underMouse;	// widget under the mouse pointer
     
     Mutex mutex;			// lock on methods for use outside the package.
 }