Mercurial > projects > mde
diff mde/gui/WidgetManager.d @ 113:9824bee909fd
Popup menu; works for simple menus except that clicking an item doesn't close it.
Revised popup support a bit; EnumContentWidget is broken and due to be replaced.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Fri, 19 Dec 2008 10:32:28 +0000 |
parents | fe061009029d |
children | b16a534f5302 |
line wrap: on
line diff
--- a/mde/gui/WidgetManager.d Sat Dec 13 12:54:43 2008 +0000 +++ b/mde/gui/WidgetManager.d Fri Dec 19 10:32:28 2008 +0000 @@ -35,6 +35,7 @@ import tango.core.sync.Mutex; import tango.util.log.Log : Log, Logger; import tango.util.container.CircularList; // pop-up draw callbacks +import tango.util.container.SortedMap; private Logger logger; static this () { @@ -62,6 +63,8 @@ super(file); Screen.addDrawable (this); + clickCallbacks = new typeof(clickCallbacks); + motionCallbacks = new typeof(motionCallbacks); } // this() runs during static this(), when imde.input doesn't exist. init() runs later. @@ -99,11 +102,12 @@ // 1. Callbacks have the highest priority recieving events (e.g. a button release) foreach (dg; clickCallbacks) - // See IWidgetManager.addClickCallback's documentation: if (dg (cast(wdabs)cx, cast(wdabs)cy, b, state)) return; // 2. Then pop-ups - IChildWidget widg; + static IChildWidget[] removedPopupParents; + uint removedPopups = 0; + IChildWidget widg; // widget clicked on { auto i = popups.iterator; foreach (popup; i) with (popup) { @@ -111,6 +115,9 @@ cy < y || cy >= y + h) { i.remove; requestRedraw; + if (removedPopupParents.length <= removedPopups) + removedPopupParents.length = removedPopupParents.length * 2 + 4; + removedPopupParents[removedPopups++] = parent; } else { widg = widget.getWidget (cast(wdabs)cx,cast(wdabs)cy); break; @@ -133,19 +140,40 @@ imde.input.setLetterCallback (&widg.keyEvent); } } + + // Tell parents their popups closed (needs to be after clickEvent for PopupMenuWidget) + while (removedPopups) + removedPopupParents[--removedPopups].popupRemoved; } /** For mouse motion events. * * Sends the event on to all motion callbacks. */ - void motionEvent (ushort cx, ushort cy) { + void motionEvent (ushort scx, ushort scy) { debug scope (failure) logger.warn ("motionEvent: failed!"); mutex.lock; scope(exit) mutex.unlock; - + wdabs cx = cast(wdabs) scx, cy = cast(wdabs) scy; foreach (dg; motionCallbacks) - dg (cast(wdabs)cx, cast(wdabs)cy); + dg (cx, cy); + + IChildWidget ohighlighted = highlighted; + foreach (popup; popups) with (popup) { + if (cx >= x && cx < x+w && cy >= y && cy < y+h) { + highlighted = widget.getWidget (cx,cy); + goto foundPopup; + } + } + highlighted = null; // not over a popup + foundPopup: + if (ohighlighted != highlighted) { + if (ohighlighted) + ohighlighted.highlight (false); + if (highlighted) + highlighted.highlight (true); + requestRedraw; + } } @@ -173,32 +201,38 @@ return rend; } - /** Place a pop-up widget near px,py. + /** Place a pop-up widget (widg) above or below parent. * * WidgetManager sets its position, draws it, passes it click events and removes it; other - * functionality should be handled by the widget's parent. */ - void addPopup (wdabs px, wdabs py, IChildWidget widg) { - ActivePopup popup; - with (popup) { - widget = widg; - w = widg.width; - h = widg.height; - x = px + w > this.w ? this.w - w : px; - if (x < 0) x = 0; - y = py + h > this.h ? this.h - h : py; - if (y < 0) y = 0; - widget.setPosition (x, y); - } - popups.prepend (popup); + * functionality should be handled by the widget's parent. + * + * Popups currently should not change their size while active. */ + void addPopup (IChildWidget parent, IChildWidget widg) { + debug assert (parent && widg, "addPopup: null widget"); + ActivePopup popup; + popup.parent = parent; + with (popup) { + widget = widg; + w = widg.width; + h = widg.height; + 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.prepend (popup); + requestRedraw; } - void removePopup (IChildWidget widg) { //FIXME: not optimal (maybe change popups though?) + /+ Not required but possibly useful later. Not optimal. + void removePopup (IChildWidget widg) { auto i = popups.iterator; foreach (popup; i) { if (popup.widget is widg) i.remove; } requestRedraw; - } + }+/ void requestRedraw () { imde.mainSchedule.request(imde.SCHEDULE.DRAW); @@ -211,8 +245,8 @@ motionCallbacks[dg.ptr] = dg; } void removeCallbacks (void* frame) { - clickCallbacks.remove(frame); - motionCallbacks.remove(frame); + clickCallbacks.removeKey(frame); + motionCallbacks.removeKey(frame); } //END IWidgetManager methods @@ -250,15 +284,17 @@ private: struct ActivePopup { IChildWidget widget; + IChildWidget parent; wdabs x,y; wdsize w,h; } IRenderer rend; CircularList!(ActivePopup) popups;// Pop-up [menus] to draw. First element is top popup. - // callbacks indexed by their frame pointers: - bool delegate(wdabs cx, wdabs cy, ubyte b, bool state) [void*] clickCallbacks; - void delegate(wdabs cx, wdabs cy) [void*] motionCallbacks; + // 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 }