# HG changeset patch # User Diggory Hardy # Date 1242936932 -7200 # Node ID ccd01fde535e4963f9f9675f677cfa5bda8a44d2 # Parent b06b04c75e866d649872e7ae7d53940a27ec892b 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). diff -r b06b04c75e86 -r ccd01fde535e mde/gui/WidgetManager.d --- 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. } diff -r b06b04c75e86 -r ccd01fde535e mde/gui/widget/AChildWidget.d --- 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; diff -r b06b04c75e86 -r ccd01fde535e mde/gui/widget/Floating.d --- 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) diff -r b06b04c75e86 -r ccd01fde535e mde/gui/widget/Ifaces.d --- 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: diff -r b06b04c75e86 -r ccd01fde535e mde/gui/widget/layout.d --- 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