changeset 160:ccd01fde535e

Replaced WidgetManager's click and motion callbacks with a drag event system. This is less flexible, but much closer to what is required (and is simpler and less open to bugs through unintended use).
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 21 May 2009 22:15:32 +0200
parents b06b04c75e86
children e3fe6acc16fb
files mde/gui/WidgetManager.d mde/gui/widget/AChildWidget.d mde/gui/widget/Floating.d mde/gui/widget/Ifaces.d mde/gui/widget/layout.d
diffstat 5 files changed, 117 insertions(+), 132 deletions(-) [+]
line wrap: on
line diff
--- a/mde/gui/WidgetManager.d	Thu May 21 20:55:10 2009 +0200
+++ b/mde/gui/WidgetManager.d	Thu May 21 22:15:32 2009 +0200
@@ -69,9 +69,6 @@
      * Params:
      *	name = The file name of the config for this GUI (to identify multiple GUIs). */
     protected this (char[] name) {
-        clickCallbacks = new typeof(clickCallbacks);
-        motionCallbacks = new typeof(motionCallbacks);
-        
         auto p = "MiscOptions.l10n" in Content.allContent;
         assert (p, "MiscOptions.l10n not created!");
         p.addCallback (&reloadStrings);
@@ -268,17 +265,6 @@
     void requestRedraw () {
         imde.mainSchedule.request(imde.SCHEDULE.DRAW);
     }
-    
-    void addClickCallback (bool delegate(wdabs, wdabs, ubyte, bool) dg) {
-        clickCallbacks[dg.ptr] = dg;
-    }
-    void addMotionCallback (void delegate(wdabs, wdabs) dg) {
-        motionCallbacks[dg.ptr] = dg;
-    }
-    void removeCallbacks (void* frame) {
-        clickCallbacks.removeKey(frame);
-        motionCallbacks.removeKey(frame);
-    }
     //END IWidgetManager methods
     
     debug void logWidgetSize (Content) {
@@ -304,12 +290,17 @@
     void wmMouseClick (wdabs cx, wdabs cy, ubyte b, bool state) {
 	if (child is null) return;
 	
-	// Callbacks have the highest priority receiving events (e.g. a button release)
-	foreach (dg; clickCallbacks)
-	    if (dg (cx, cy, b, state)) return;
-	    
-	    // Update underMouse to get the widget clicked on
-	    updateUnderMouse (cx, cy, state);
+	// Update underMouse to get the widget clicked on
+	updateUnderMouse (cx, cy, state);
+	
+	// end of a drag?
+	if (dragStart !is null && b == 1 && state == false) {
+	    if (dragStart.dragRelease (cx, cy, underMouse)) {
+		dragStart = null;
+		return;
+	    }
+	    dragStart = null;
+	}
 	
 	// Disable keyboard input if on another widget:
 	if (keyFocus && keyFocus !is underMouse) {
@@ -327,22 +318,25 @@
 	    popupContext.setup (0, 3);
 	    positionPopup (underMouse, popupContext);
 	    requestRedraw;
-	} else	// post other button presses to clickEvent
-	    if (underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) {
-		// keyboard input requested
+	} else {	// post other button presses to clickEvent
+	    int ret = underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state);
+	    if (ret & 1) {	// keyboard input requested
 		keyFocus = underMouse;
 		setLetterCallback (&underMouse.keyEvent);
 	    }
+	    if (ret & 2)	// drag events requested
+		dragStart = underMouse;
+	}
     }
     
     /** For mouse motion events.
      *
      * Lock on mutex before calling. Pass new mouse coordinates. */
     void wmMouseMotion (wdabs cx, wdabs cy) {
-	foreach (dg; motionCallbacks)
-	    dg (cx, cy);
+	updateUnderMouse (cx, cy, false);
 	
-	updateUnderMouse (cx, cy, false);
+	if (dragStart !is null)
+	    dragStart.dragMotion (cx, cy, underMouse);
     }
     
     
@@ -504,11 +498,9 @@
     // Popup(s) handled directly by AWidgetManager:
     IChildWidget popupContext;		// context menu (active if not null)
     
-    // 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
-    IChildWidget underMouse;	// widget under the mouse pointer
+    IChildWidget dragStart;		// if non-null, this widget should receive motion and click-release events
+    IChildWidget keyFocus;		// widget receiving keyboard input
+    IChildWidget underMouse;		// widget under the mouse pointer
     
     Mutex mutex;			// lock on methods for use outside the package.
 }
--- a/mde/gui/widget/AChildWidget.d	Thu May 21 20:55:10 2009 +0200
+++ b/mde/gui/widget/AChildWidget.d	Thu May 21 22:15:32 2009 +0200
@@ -131,6 +131,12 @@
 	return 0;
     }
     
+    /* Dummy functions; many widgets don't need to respond to dragging. */
+    void dragMotion (wdabs cx, wdabs cy, IChildWidget) {}
+    bool dragRelease (wdabs cx, wdabs cy, IChildWidget) {
+	return false;	// any widgets not handling events should let them be passed as normal to clickEvent
+    }
+    
     /* Dummy functions: suitable for widgets with no text input. */
     override void keyEvent (ushort, char[]) {}
     override void keyFocusLost () {}
@@ -264,8 +270,7 @@
         } else if (state) {
             pushed = true;
             mgr.requestRedraw;
-            mgr.addClickCallback (&clickWhilePushed);
-            mgr.addMotionCallback (&motionWhilePushed);
+	    return 2;
         }
 	return 0;
     }
@@ -278,23 +283,19 @@
     }
     
     /// Called when a mouse click event occurs while held; handles up-click
-    bool clickWhilePushed (wdabs cx, wdabs cy, ubyte b, bool state) {
-        if (b == 1 && state == false) {
-            if (cx >= x && cx < x+w && cy >= y && cy < y+h) {	// button event
-                parentIPPW.menuDone;
-                activated();
-            }
-            
-            pushed = false;
-            mgr.requestRedraw;
-            mgr.removeCallbacks (cast(void*) this);
-            
-            return true;
-        }
-        return false;
+    override bool dragRelease (wdabs cx, wdabs cy, IChildWidget target) {
+	if (pushed) {	// on button
+	    parentIPPW.menuDone;
+	    activated();
+	}
+	
+	pushed = false;
+	mgr.requestRedraw;
+	
+	return true;
     }
     /// Called when a mouse motion event occurs while held; handles pushing in/out on hover
-    void motionWhilePushed (wdabs cx, wdabs cy) {
+    override void dragMotion (wdabs cx, wdabs cy, IChildWidget) {
         bool oldPushed = pushed;
         if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true;
         else pushed = false;
--- a/mde/gui/widget/Floating.d	Thu May 21 20:55:10 2009 +0200
+++ b/mde/gui/widget/Floating.d	Thu May 21 22:15:32 2009 +0200
@@ -209,46 +209,19 @@
                     else if (resizeType & RESIZE.Y2)
                         yDrag = h - cy;
                     
-                    mgr.addClickCallback (&endCallback);
-                    mgr.addMotionCallback (&resizeCallback);
+                    return 2;
                 } else if (borderType & BTYPE.MOVE) {   // window is being moved
                     xDrag = cx - x;
                     yDrag = cy - y;
                     
-                    mgr.addClickCallback (&endCallback);
-                    mgr.addMotionCallback (&moveCallback);
+                    return 2;
                 }
             }
         }
 	return 0;
     }
-    
-protected:
-    /** Return the index of the floating object under (cx,cy). If no widget is,
-     * return size_t.max.
-     * 
-     * Params:
-     *	raiseWidget	= if true, the widget returned is raised to be the top-
-     *			most floating widget. */
-    size_t getFloatingWidget (wdim cx, wdim cy, bool raiseWidget = false) {
-	debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)");
-	
-	foreach_reverse (j,i; sWOrder) with (sWData[i]) {
-	    wdim lx = cx - (this.x + x);
-	    wdim ly = cy - (this.y + y);
-	    if (lx >= 0 && lx < w && ly >= 0 && ly < h) {
-		if (raiseWidget) {
-		    sWOrder[j..$-1] = sWOrder[j+1..$].dup;
-		    sWOrder[$-1] = i;
-		    mgr.requestRedraw;
-		}
-		return i;
-	    }
-	}
-	return size_t.max;	// no match
-    }
-    
-    void moveCallback (wdabs cx, wdabs cy) {
+    void dragMotion (wdabs cx, wdabs cy, IChildWidget) {
+    if (resizeType == RESIZE.NONE) {
         with (sWData[active]) {
                 x = cx-xDrag;
                 y = cy-yDrag;
@@ -265,8 +238,7 @@
                 subWidgets[active].setPosition (this.x + x + border.x1, this.y + y + border.y1);
         }
         mgr.requestRedraw;
-    }
-    void resizeCallback (wdabs cx, wdabs cy) {
+    } else {
         with (sWData[active]) {
             if (resizeType & RESIZE.X1) {
                 wdim ow = w;
@@ -309,14 +281,37 @@
         }
         mgr.requestRedraw;
     }
-    bool endCallback (wdabs, wdabs, ubyte b, bool state) {
-        if (b == 1 && state == false) { // end of a move/resize
-            mgr.removeCallbacks (cast(void*) this);
-            return true;    // we've handled the up-click
-        }
-        return false;       // we haven't handled it
+    }
+    bool dragRelease (wdabs, wdabs, IChildWidget) {
+	return true;    // we've handled the up-click
     }
     
+protected:
+    /** Return the index of the floating object under (cx,cy). If no widget is,
+     * return size_t.max.
+     * 
+     * Params:
+     *	raiseWidget	= if true, the widget returned is raised to be the top-
+     *			most floating widget. */
+    size_t getFloatingWidget (wdim cx, wdim cy, bool raiseWidget = false) {
+	debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)");
+	
+	foreach_reverse (j,i; sWOrder) with (sWData[i]) {
+	    wdim lx = cx - (this.x + x);
+	    wdim ly = cy - (this.y + y);
+	    if (lx >= 0 && lx < w && ly >= 0 && ly < h) {
+		if (raiseWidget) {
+		    sWOrder[j..$-1] = sWOrder[j+1..$].dup;
+		    sWOrder[$-1] = i;
+		    mgr.requestRedraw;
+		}
+		return i;
+	    }
+	}
+	return size_t.max;	// no match
+    }
+    
+    
     struct SWData {     // NOTE: x,y,w,h must be first elements; search (&d.x)
         wdim x,y;       // position (corner of border)
         wdim w,h;       // size (including border)
--- a/mde/gui/widget/Ifaces.d	Thu May 21 20:55:10 2009 +0200
+++ b/mde/gui/widget/Ifaces.d	Thu May 21 22:15:32 2009 +0200
@@ -237,24 +237,6 @@
     * Normally specific to the GUI, but widgets have no direct contact with the GUI and this
     * provides the possibility of per-window renderers (if desired). */
     IRenderer renderer ();
-    
-    // User input:
-    /** Add a mouse click callback.
-     * 
-     * This is a delegate this will be called for all mouse click events recieved by the gui, not
-     * simply all click events on the widget (as clickEvent recieves).
-     *
-     * The delegate should return true if it accepts the event and no further processing is
-     * required (i.e. the event should not be handled by anything else), false otherwise. */
-    void addClickCallback (bool delegate (wdabs cx, wdabs cy, ubyte b, bool state) dg);
-    
-    /** Add a mouse motion callback: delegate will be called for all motion events recieved by the
-     * gui. */
-    void addMotionCallback (void delegate (wdabs cx, wdabs cy) dg);
-    
-    /** Remove all event callbacks on this widget (according to the delegate's .ptr). */
-    // Note: don't try to pass a reference and cast to void* in the function; it's a different address.
-    void removeCallbacks (void* frame);
 }
 
 
@@ -397,9 +379,30 @@
      *
      * Widget may assume coordinates are on the widget (caller must check).
      *
-     * The return value has the following flags: 1 to request keyboard input. */
+     * The return value has the following flags:
+     * $(TABLE
+     * $(TR $(TD 1) $(TD Request keyboard input))
+     * $(TR $(TD 2) $(TD Request the functions dragMotion and dragRelease are called))
+     * ) */
     int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state);
     
+    /** Called when dragging motion occurs, originating from this widget.
+     *
+     * Params: target = The widget under the mouse when the click was released
+     *
+     * Only called if requested by clickEvent. */
+    void dragMotion (wdabs cx, wdabs cy, IChildWidget target);
+    
+    /** Called at the end of a drag which originated from this widget.
+     *
+     * Params: target = The widget under the mouse when the click was released
+     *
+     * Returns: true if the up-click event should not be passed to
+     * clickEvent on the relevent widget.
+     *
+     * Only called if requested by clickEvent. */
+    bool dragRelease (wdabs cx, wdabs cy, IChildWidget target);
+    
     /** Receives keyboard events when requested.
      *
      * Params:
--- a/mde/gui/widget/layout.d	Thu May 21 20:55:10 2009 +0200
+++ b/mde/gui/widget/layout.d	Thu May 21 22:15:32 2009 +0200
@@ -314,12 +314,30 @@
             dragX = cx;
             dragY = cy;
             
-            mgr.addClickCallback (&endCallback);
-            mgr.addMotionCallback (&resizeCallback);
+	    return 2;
         }
 	return 0;
     }
     
+    //BEGIN Col/row resizing callback
+    override void dragMotion (wdim cx, wdim cy, IChildWidget) {
+        col.resizeCols (cx - dragX);
+        row.resizeCols (cy - dragY);
+        
+        // NOTE: all adjustments are relative; might be better if they were absolute?
+        dragX = cx;
+        dragY = cy;
+        
+        foreach (i,widget; subWidgets)
+            widget.setPosition (x + col.pos[i % cols],
+                                y + row.pos[i / cols]);
+        mgr.requestRedraw;
+    }
+    override bool dragRelease (wdabs cx, wdabs cy, IChildWidget) {
+	return true;	// we've handled the up-click
+    }
+    //END Col/row resizing callback
+    
     override void draw () {
         super.draw ();
         
@@ -427,33 +445,9 @@
         parent.minHChange (this, row.mw);
     }
     
-    
-    //BEGIN Col/row resizing callback
-    override void resizeCallback (wdim cx, wdim cy) {
-        col.resizeCols (cx - dragX);
-        row.resizeCols (cy - dragY);
-        
-        // NOTE: all adjustments are relative; might be better if they were absolute?
-        dragX = cx;
-        dragY = cy;
-        
-        foreach (i,widget; subWidgets)
-            widget.setPosition (x + col.pos[i % cols],
-                                y + row.pos[i / cols]);
-        mgr.requestRedraw;
-    }
-    override bool endCallback (wdabs cx, wdabs cy, ubyte b, bool state) {
-        if (b == 1 && state == false) {
-            mgr.removeCallbacks (cast(void*) this);
-            return true;	// we've handled the up-click
-        }
-        return false;		// we haven't handled it
-    }
-    
 protected:
     // Data for resizing cols/rows:
     wdim dragX, dragY;	// coords where drag starts
-    //END Col/row resizing callback
     
     size_t cols, rows;	// number of cells in grid
     wdim[] initWidths;  // see this / setInitialSize