# HG changeset patch # User Diggory Hardy # Date 1248599057 -7200 # Node ID 7f7b2011b759aa3b454086677e5abc518123916d # Parent e45226d3deaea8d6e455c5bb78c49fb4b4ca005a Partially complete commit: code runs but context menus don't work. Moved WMScreen.createRootWidget to WidgetManager.createWidgets. Put childContext under a popupHandler widget. TODO: implement IChildWidget.setContent(Content) (see AParentWidget.d:237). diff -r e45226d3deae -r 7f7b2011b759 codeDoc/ideas.txt --- a/codeDoc/ideas.txt Mon Jun 29 21:20:16 2009 +0200 +++ b/codeDoc/ideas.txt Sun Jul 26 11:04:17 2009 +0200 @@ -9,6 +9,9 @@ GUI: -> Widgets: + > generalise concept of 2D widgets: + > some other way of specifying a widget's dimensions + > divide layout widget into a sub-widget handler and a placement component, such that placement can use a rectangle, hexagon or whatever component -> scripted widgets -> decent rendering/theme system -> events: diff -r e45226d3deae -r 7f7b2011b759 data/conf/guiDemo.mtt --- a/data/conf/guiDemo.mtt Mon Jun 29 21:20:16 2009 +0200 +++ b/data/conf/guiDemo.mtt Sun Jul 26 11:04:17 2009 +0200 @@ -46,7 +46,7 @@ - + diff -r e45226d3deae -r 7f7b2011b759 mde/content/ServiceContent.d --- a/mde/content/ServiceContent.d Mon Jun 29 21:20:16 2009 +0200 +++ b/mde/content/ServiceContent.d Sun Jul 26 11:04:17 2009 +0200 @@ -26,7 +26,7 @@ import mde.content.AStringContent; /** Interface for ServiceContent and ServiceContentList. */ -interface IServiceContent { +interface IServiceContent : IContent { void setContent (Content cont); } @@ -87,54 +87,54 @@ } // Doesn't support directly setting the value override void opAssign (bool val) {} + + + /** Create all services. + * + * Each WidgetManager has it's own instance, but these share one clipboard, etc. + * + * Currently all clipboard operations convert to/from a string. + * TODO: Possible extensions: multi-item clipboard displaying value in menu, + * copying without converting everything to/from a string. */ + static ServiceContentList createItems (char[] name) { + char[] lName = "menus.services."~name; + auto ret = new ServiceContentList (lName); + + // Many use callbacks on a generic class type. For this, we should be sure of the type. + (new AStringService(lName~".copy")).addCallback ((IContent c) { + debug assert (cast(AStringService)c); + with (cast(AStringService)c) { + if (activeCont !is null) + clipboard = activeCont.toString(0); + } + }); + (new AStringService(lName~".paste")).addCallback ((IContent c) { + debug assert (cast(AStringService)c); + with (cast(AStringService)c) { + if (activeCont !is null) + activeCont = clipboard; + } + }); + + new ServiceContentList(lName~".calculator"); + (new IntService(lName~".calculator.increment")).addCallback ((IContent c) { + debug assert (cast(IntService)c); + with (cast(IntService)c) { + if (activeCont !is null) + activeCont = activeCont()+1; + } + }); + (new IntService(lName~".calculator.decrement")).addCallback ((IContent c) { + debug assert (cast(IntService)c); + with (cast(IntService)c) { + if (activeCont !is null) + activeCont = activeCont()-1; + } + }); + return ret; + } + +private: + static char[] clipboard; + //TODO: lock on clipboard: static Mutex mutex; } - -/** Create all services. -* -* For now, all are under the menu. -* -* Currently all clipboard operations convert to/from a string. -* TODO: Possible extensions: multi-item clipboard displaying value in menu, -* copying without converting everything to/from a string. */ -static this () { - // Create ContentLists so they have the correct type. - new ServiceContentList ("menus.services"); - // Many use callbacks on a generic class type. For this, we should be sure of the type. - auto copy = new AStringService("menus.services.copy"); - copy.addCallback ((IContent c) { - debug assert (cast(AStringService)c); - with (cast(AStringService)c) { - if (activeCont !is null) - clipboard = activeCont.toString(0); - } - }); - auto paste = new AStringService("menus.services.paste"); - paste.addCallback ((IContent c) { - debug assert (cast(AStringService)c); - with (cast(AStringService)c) { - if (activeCont !is null) - activeCont = clipboard; - } - }); - - new ServiceContentList("menus.services.calculator"); - auto inc = new IntService("menus.services.calculator.increment"); - inc.addCallback ((IContent c) { - debug assert (cast(IntService)c); - with (cast(IntService)c) { - if (activeCont !is null) - activeCont = activeCont()+1; - } - }); - auto dec = new IntService("menus.services.calculator.decrement"); - dec.addCallback ((IContent c) { - debug assert (cast(IntService)c); - with (cast(IntService)c) { - if (activeCont !is null) - activeCont = activeCont()-1; - } - }); -} -private { - char[] clipboard; -} diff -r e45226d3deae -r 7f7b2011b759 mde/gui/WMScreen.d --- a/mde/gui/WMScreen.d Mon Jun 29 21:20:16 2009 +0200 +++ b/mde/gui/WMScreen.d Sun Jul 26 11:04:17 2009 +0200 @@ -24,7 +24,6 @@ import mde.gui.WidgetManager; import mde.gui.WidgetLoader; import mde.gui.widget.Ifaces; -import mde.gui.renderer.createRenderer; import mde.setup.Screen; import mde.input.Input; @@ -68,6 +67,10 @@ input.addMouseClickCallback(&clickEvent) .addMouseMotionCallback(&motionEvent); } + ~this () { + // Make sure the keyboard is not locked in text-entry mode. + input.setLetterCallback (null); + } /** Draw the gui. */ void draw() { @@ -100,27 +103,22 @@ } } - + /** Notification of externally-caused screen resize. + * + * Should be called before createWidgets to prevent widgets being squashed + * to min-dims on loading (losing saved dimensions of columns, etc). */ void sizeEvent (int nw, int nh) { // IDrawable function mutex.lock; scope(exit) mutex.unlock; w = cast(wdim) nw; h = cast(wdim) nh; + matchMinimalSize; - if (w < mw) { - logger.warn ("Min width for gui, {}, not met: {}", mw, w); - w = mw; - } - if (h < mh) { - logger.warn ("Min height for gui, {}, not met: {}", mh, h); - h = mh; - } - - if (!child) return; // if not created yet. - child.setWidth (w, -1); - child.setHeight (h, -1); - child.setPosition (0,0); + if (!childRoot) return; // if not created yet. + childRoot.setWidth (w, -1); + childRoot.setHeight (h, -1); + childRoot.setPosition (0,0); } protected: @@ -128,36 +126,6 @@ input.setLetterCallback (dlg); } - /* Second stage of widget loading. - * Note: sizeEvent should be called with window size before this. */ - final override void createRootWidget () { - // The renderer needs to be created on the first load, but not after this. - if (rend is null) - rend = createRenderer (rendName); - - debug (mdeWidgets) logger.trace ("Creating root widget..."); - child = makeWidget (this, "root"); - debug (mdeWidgets) logger.trace ("Setting up root widget..."); - child.setup (0, 3); - - mw = child.minWidth; - mh = child.minHeight; - if (w < mw) { - logger.warn ("Min width for gui, {}, not met: {}", mw, w); - w = mw; - } - if (h < mh) { - logger.warn ("Min height for gui, {}, not met: {}", mh, h); - h = mh; - } - - debug (mdeWidgets) logger.trace ("Setting size and position of root widget..."); - child.setWidth (w, -1); - child.setHeight (h, -1); - child.setPosition (0,0); - debug (mdeWidgets) logger.trace ("Done creating root widget."); - } - final override void preSave () { if (keyFocus) { keyFocus.keyFocusLost; diff -r e45226d3deae -r 7f7b2011b759 mde/gui/WidgetLoader.d --- a/mde/gui/WidgetLoader.d Mon Jun 29 21:20:16 2009 +0200 +++ b/mde/gui/WidgetLoader.d Sun Jul 26 11:04:17 2009 +0200 @@ -157,8 +157,7 @@ } // Create the widgets: - createRootWidget; - underMouse = child; // must be something + createWidgets; } /** Save changes, if any exist. @@ -171,7 +170,7 @@ scope(exit) mutex.unlock; // Make all widgets save any changed data: - child.saveChanges; + saveChanges; if (changes.noChanges) return; @@ -223,24 +222,8 @@ protected: // Called by derived classes, not thread safe for the same instance //BEGIN WidgetManagement methods - /** Second stage of loading the widgets. - * - * loadDesign handles the data; this method needs to: - * --- - * // 1. Create the root widget: - * child = makeWidget ("root"); - * child.setup (0, 3); - * // 2. Set the size: - * child.setWidth (child.minWidth, 1); - * child.setHeight (child.minHeight, 1); - * // 3. Set the position (necessary part of initialization): - * child.setPosition (0,0); - * --- - */ - void createRootWidget(); - - /** Called before saving (usually when the GUI is about to be destroyed, although not - * necessarily). */ + /** Called before saving to make sure no data is being edited (and thus + * would not be saved). */ void preSave (); //END WidgetManagement methods diff -r e45226d3deae -r 7f7b2011b759 mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Mon Jun 29 21:20:16 2009 +0200 +++ b/mde/gui/WidgetManager.d Sun Jul 26 11:04:17 2009 +0200 @@ -26,6 +26,7 @@ import mde.gui.WidgetDataSet; import mde.gui.widget.Ifaces; +import mde.gui.renderer.createRenderer; import imde = mde.imde; import mde.content.Content; @@ -41,6 +42,7 @@ import mde.gui.widget.miscContent; import mde.gui.widget.Floating; import mde.gui.widget.ParentContent; +import mde.gui.widget.AParentWidget; public import tango.core.sync.Mutex; import tango.util.log.Log : Log, Logger; @@ -75,9 +77,8 @@ assert (p, "MiscOptions.l10n not created!"); p.addCallback (&reloadStrings); - serviceContent = cast (IServiceContent) Content.get ("menus.services"); - assert (Content.get ("menus.services")); - assert (serviceContent !is null, "Content service menu doesn't exist or has wrong type"); + serviceContent = ServiceContentList.createItems (name); + assert (cast (IServiceContent) Content.get ("menus.services."~name)); debug { // add a debug-mode menu auto lWS = new EventContent ("menus.debug."~name~".logWidgetSize"); @@ -92,33 +93,43 @@ final void recursionCheck (widgetID, IContent) {} override void minWChange (IChildWidget widget, wdim nmw) { - if (widget !is child) // Usually because widget is a floating widget - // This may get called from a CTOR, hence we can't check widget is one of popupContext, etc. + if (widget !is childRoot) // Probably because widget is a popup widget + // This may get called from a CTOR, hence we can't check widget is one of childContext, etc. return; mw = nmw; if (w < nmw) { - child.setWidth (nmw, -1); + childRoot.setWidth (nmw, -1); w = nmw; } - child.setPosition (0,0); + childRoot.setPosition (0,0); requestRedraw; } override void minHChange (IChildWidget widget, wdim nmh) { - if (widget !is child) + if (widget !is childRoot) return; mh = nmh; if (h < nmh) { - child.setHeight (nmh, -1); + childRoot.setHeight (nmh, -1); h = nmh; } - child.setPosition (0,0); + childRoot.setPosition (0,0); requestRedraw; } + //END IParentWidget methods + + //BEGIN IWidget methods + override bool saveChanges () { + bool ret = childRoot.saveChanges; + ret |= childContext.saveChanges; + if (childDragged !is null) + ret |= childDragged.saveChanges; + return ret; + } override bool dropContent (IContent content) { return false; } - //END IParentWidget methods + //END IWidget methods //BEGIN IPopupParentWidget methods override IPopupParentWidget getParentIPPW () { @@ -126,10 +137,14 @@ } override void addChildIPPW (IPopupParentWidget ippw) { + requestRedraw; + if (ippw is childContext) { // special handling - a separate IPPW + contextActive = true; + return; + } if (childIPPW) childIPPW.removedIPPW; childIPPW = ippw; - requestRedraw; } override bool removeChildIPPW (IPopupParentWidget ippw) { if (childIPPW !is ippw) return false; @@ -144,6 +159,8 @@ mAIPPW = mA; if (childIPPW) childIPPW.menuActive = mA; + if (contextActive) + childContext.menuActive = mA; } override MenuPosition menuActive () { return mAIPPW; @@ -152,31 +169,24 @@ return MenuPosition.INACTIVE; } - override void menuDone () { - // close context menu, but not childIPPW - if (childIPPW is null) - menuActive = MenuPosition.INACTIVE; - popupContext = null; - requestRedraw; - } + // Note: also triggered by non-popup widgets + override void menuDone () {} override IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup) { - if (popupContext) { - if (popupContext.onSelf (cx, cy)) - return popupContext.getWidget (cx, cy); - if (closePopup) { - if (childIPPW is null) - menuActive = MenuPosition.INACTIVE; - popupContext = null; - requestRedraw; - } + IChildWidget ret; + // Don't bother with childDragged; it has no interaction + if (contextActive) { + ret = childContext.getPopupWidget (cx, cy, closePopup); + if (ret) return ret; + if (closePopup) { + childContext.removedIPPW; + requestRedraw; + } } if (childIPPW) { - IChildWidget ret = - childIPPW.getPopupWidget (cx, cy, closePopup); + ret = childIPPW.getPopupWidget (cx, cy, closePopup); if (ret) return ret; if (closePopup) { - menuActive = MenuPosition.INACTIVE; removeChildIPPW (childIPPW); } } @@ -184,13 +194,15 @@ } override void drawPopup () { - if (popupContext) - popupContext.draw(); - if (dragContentDisplay) - dragContentDisplay.draw(); + if (contextActive) + childContext.draw(); + if (childDragged) + childDragged.draw(); } debug protected override bool isChild (IPopupParentWidget ippw) { + if (contextActive && ippw is childContext) + return true; return ippw is childIPPW; } @@ -293,16 +305,53 @@ debug void logWidgetSize (IContent) { logger.trace ("size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh); - child.logWidgetSize; + logger.trace ("childRoot:"); + childRoot.logWidgetSize; + logger.trace ("childContext:"); + childContext.logWidgetSize; + if (childDragged !is null) { + logger.trace ("childDragged:"); + childDragged.logWidgetSize; + } } protected: // These methods are called by derived classes to do the widget-management work //BEGIN WidgetManagement methods + /** Second stage of widget loading. + * + * Widget data should be loaded before this is called. */ + final void createWidgets () { + // The renderer needs to be created on the first load, but not after this. + if (rend is null) + rend = createRenderer (rendName); + + debug (mdeWidgets) logger.trace ("Creating root widget..."); + childRoot = makeWidget (this, "root"); + debug (mdeWidgets) logger.trace ("Setting up root widget..."); + childRoot.setup (0, 3); + + mw = childRoot.minWidth; + mh = childRoot.minHeight; + matchMinimalSize (); + + debug (mdeWidgets) logger.trace ("Setting size and position of root widget..."); + childRoot.setWidth (w, -1); + childRoot.setHeight (h, -1); + childRoot.setPosition (0,0); + debug (mdeWidgets) logger.trace ("Done creating root widget."); + + childContext = new PopupHandlerWidget (this, this, "contextHandler", "context", serviceContent); + childContext.setup (0,3); + debug (mdeWidgets) logger.trace ("Created context handler widget."); + + underMouse = childRoot; // must be something + } + /** Draw all widgets */ - void wmDrawWidgets() { - if (child) - child.draw; + final void wmDrawWidgets() { + if (childRoot) + childRoot.draw; if (childIPPW) childIPPW.drawPopup; drawPopup; @@ -311,8 +360,8 @@ /** For mouse click events. * * Sends the event on to the relevant windows and all click callbacks. */ - void wmMouseClick (wdabs cx, wdabs cy, ubyte b, bool state) { - if (child is null) return; + final void wmMouseClick (wdabs cx, wdabs cy, ubyte b, bool state) { + if (childRoot is null) return; // Update underMouse to get the widget clicked on updateUnderMouse (cx, cy, state); @@ -321,7 +370,7 @@ if (dragStart !is null && b == dragButton && state == false) { IChildWidget dS = dragStart; dragStart = null; - dragContentDisplay = null; + childDragged = null; requestRedraw; if (dS.dragRelease (cx, cy, underMouse)) return; @@ -336,16 +385,11 @@ // Finally, post the actual event: if (b == 3 && state) { // right click - open context menu - if (popupContext !is null) return; Content contextContent = cast(Content)underMouse.content; - if (contextContent is null) return; - // NOTE: Creates new widgets every time; not optimal - serviceContent.setContent (contextContent); - popupContext = makeWidget (this, "context", contextContent); - popupContext.setup (0, 3); - //NOTE: usually set parentIPPW.menuActive: - menuActive = positionPopup (underMouse, popupContext); - requestRedraw; + if (contextContent !is null) { + serviceContent.setContent (contextContent); + childContext.openMenu (underMouse, contextContent); + } } 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 @@ -358,11 +402,11 @@ if (ret & 4) { IContent c = underMouse.content(); if (c) { // NOTE: creates a new widget, not optimal - dragContentDisplay = new DisplayContentWidget (this, this, "dragContentDisplay", WidgetData ([0], []), c); - dragContentDisplay.setup (0, 3); + childDragged = new DisplayContentWidget (this, this, "dragContentDisplay", WidgetData ([0], []), c); + childDragged.setup (0, 3); dragX = underMouse.xPos - cx; dragY = underMouse.yPos - cy; - dragContentDisplay.setPosition (cx + dragX, cy + dragY); + childDragged.setPosition (cx + dragX, cy + dragY); } } } @@ -372,13 +416,13 @@ /** For mouse motion events. * * Lock on mutex before calling. Pass new mouse coordinates. */ - void wmMouseMotion (wdabs cx, wdabs cy) { + final void wmMouseMotion (wdabs cx, wdabs cy) { updateUnderMouse (cx, cy, false); if (dragStart !is null) { dragStart.dragMotion (cx, cy, underMouse); - if (dragContentDisplay !is null) { - dragContentDisplay.setPosition (cx + dragX, cy + dragY); + if (childDragged !is null) { + childDragged.setPosition (cx + dragX, cy + dragY); requestRedraw; } } @@ -388,25 +432,25 @@ /** A change callback on MiscOptions.l10n content to update widgets. * * Relies on another callback reloading translations to content first! */ - void reloadStrings (IContent) { + final void reloadStrings (IContent) { synchronized(mutex) { - if (child is null) return; - child.setup (++setupN, 2); - child.setWidth (w, -1); - child.setHeight (h, -1); - child.setPosition (0,0); - popupContext.setup (setupN, 2); - //NOTE: does popupContext need to be re-scaled? + if (childRoot is null) return; + childRoot.setup (++setupN, 2); + childRoot.setWidth (w, -1); + childRoot.setHeight (h, -1); + childRoot.setPosition (0,0); + childContext.setup (setupN, 2); + //TODO: possibly childDragged? requestRedraw; } } // for internal use - void updateUnderMouse (wdabs cx, wdabs cy, bool closePopup) { + final 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); + debug assert (childRoot.onSelf (cx, cy), "WidgetManager: childRoot doesn't cover whole area"); + underMouse = childRoot.getWidget (cx, cy); } if (underMouse !is oUM) { debug assert (oUM && underMouse, "no widget under mouse: error"); @@ -417,6 +461,23 @@ } } + /** If possible, the screen-interaction derived class should override to + * make sure the window is at least (mw,mh) in size. In any case, this + * method MUST make sure w >= mw and h >= mh even if the window isn't this + * big. + * + * A resize may not be required when this is called, however. */ + void matchMinimalSize () { + if (w < mw) { + logger.warn ("Min width for gui, {}, not met: {}", mw, w); + w = mw; + } + if (h < mh) { + logger.warn ("Min height for gui, {}, not met: {}", mh, h); + h = mh; + } + } + /// This should be overloaded to set a callback receiving keyboard input. abstract void setLetterCallback(void delegate(ushort, char[])); //END WidgetManagement methods @@ -529,32 +590,47 @@ //END makeWidget metacode protected: - WidgetDataSet curData; // Current data - WidgetDataChanges changes; // Changes for the current design. + // Main child widget: + IChildWidget childRoot; // Root of the main GUI widget tree - char[] rendName; // Name of renderer; for saving and creating renderers - IRenderer rend; - - // Widgets: + // Dimensions and child set-up data (fit to childRoot): wdim w,h; // current widget size; should be at least (mw,mh) even if not displayable wdim mw,mh; // minimal area required by widgets - scope IChildWidget child; // The primary widget. uint setupN; // n to pass to IChildWidget.setup + // IPopupParentWidget stuff for childRoot: MenuPosition mAIPPW; // IPPW variable IPopupParentWidget childIPPW; // child IPPW, if any active - // Popup(s) handled directly by AWidgetManager: - IChildWidget popupContext; // context menu (active if not null) - IServiceContent serviceContent; // context menu content tree - IChildWidget dragContentDisplay; // displays dragged content; no interaction + IChildWidget keyFocus; // widget receiving keyboard input + IChildWidget underMouse; // widget under the mouse pointer + + // Context menu: + // Essentially, we consider childContext a full child IPPW, but handle it separately from + // childIPPW. Instead of providing another ref. for this IPPW, shortcut by using this reference + // and the boolean contextActive: + scope PopupHandlerWidget childContext; // context menu popup (handler) + bool contextActive = false; // If true, consider childContext a child IPPW + scope IServiceContent serviceContent; // context menu content tree + + + // Drag-and-drop data: + //NOTE: could be wrapped with a PopupHandlerWidget, but can't set position then? + scope IChildWidget childDragged; // displays dragged content; no interaction IChildWidget dragStart; // if non-null, this widget should receive motion and click-release events int dragButton; // index of button in use for drag wdrel dragX, dragY; // coordinates of dragged content relative to mouse - IChildWidget keyFocus; // widget receiving keyboard input - IChildWidget underMouse; // widget under the mouse pointer + + // Renderer: + char[] rendName; // Name of renderer; for saving and creating renderers + scope IRenderer rend; + + + // Data loaded/to save: + WidgetDataSet curData; // Current data + WidgetDataChanges changes; // Changes for the current design. Mutex mutex; // lock on methods for use outside the package. } diff -r e45226d3deae -r 7f7b2011b759 mde/gui/widget/AParentWidget.d --- a/mde/gui/widget/AParentWidget.d Mon Jun 29 21:20:16 2009 +0200 +++ b/mde/gui/widget/AParentWidget.d Sun Jul 26 11:04:17 2009 +0200 @@ -77,7 +77,7 @@ // 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 (wID is id) + if (wID == id) throw new WidgetRecursionException (wID); parent.recursionCheck (wID, c); } @@ -166,7 +166,6 @@ if (childIPPW) { ret = childIPPW.getPopupWidget (cx, cy, closePopup); if (closePopup && ret is null) { - menuActive = MenuPosition.INACTIVE; removeChildIPPW (childIPPW); } } @@ -210,3 +209,35 @@ IChildWidget popup; MenuPosition mAIPPW; } + +import mde.content.Content; +/****************************************************************************** + * Not "really" a widget (zero size, no direct interaction), but a handler for + * popup widgets. + * + * Intended to manage a context menu for WidgetManager, and not to work quite + * like a regular IPPW. + *****************************************************************************/ +class PopupHandlerWidget : APopupParentWidget +{ + this (IWidgetManager mgr, IParentWidget parent, widgetID id, char[] subWidg, IContent c) { + super (mgr, parent, id); + + popup = mgr.makeWidget (this, subWidg, c); + subWidgets = [popup]; + + w = mw = 0; + h = mh = 0; + } + + /// Open a context menu + void openMenu (IChildWidget underMouse, Content contextContent) { + if (mAIPPW != MenuPosition.INACTIVE) return; // already in use + //TODO: + //subWidgets[0].setContent (contextContent); + parentIPPW.addChildIPPW (this); + // For context menus, don't set parentIPPW.menuActive like for clicked menus: + menuActive = mgr.positionPopup (underMouse, popup); + mgr.requestRedraw; + } +} diff -r e45226d3deae -r 7f7b2011b759 mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Mon Jun 29 21:20:16 2009 +0200 +++ b/mde/gui/widget/Ifaces.d Sun Jul 26 11:04:17 2009 +0200 @@ -40,6 +40,16 @@ *****************************************************************************/ interface IWidget { + /** When this is called, if the widget has any changed data to save it + * should do so (see widgetData/dimData) and return true. Otherwise it + * should return false. + * + * If the widget has subwidgets, it should also be recursively called on + * these, returning true if any data was saved. + * + * Actually the return value is ignored; I think widgets still return it + * correctly though. */ + bool saveChanges (); /** Called on a widget when something is dragged onto it. * @@ -176,6 +186,15 @@ * when the menu is closed. * If set on the parent IPPW, popup menus can be opened with just a mouse- * over and buttons activated with an up-click. + * So given IPPWs U-Z (indentation shows ancestry): + * + * --- + * X (window manager): menuActive false + * Y (popup window): menuActive true + * U (menu button): menuActive true, open popup menu + * V (menu button): menuActive false, menu closed but opens on hover + * Z (menu button): menuActive false, menu closed + * --- * * It is also used by positionPopup to signal if the popup was left or * right of the previous popup, to enable a further popup to be placed by @@ -216,6 +235,12 @@ * Interface for the widget manager. * * This class handles widget rendering, input, loading and saving. + * + * The widget manager is also a popup-parent for it's main child widget + * (childRoot), working as a normal popup-parent except that parentMenuActive + * is always false. It also has two special popup children: childContext and + * childDragged, which are handled indirectly via PopupHandlerWidget so that + * their menuActive state doesn't affect childRoot or decendants. *****************************************************************************/ interface IWidgetManager : IPopupParentWidget { @@ -328,14 +353,6 @@ * (may) have changed. */ bool setup (uint n, uint flags); - /** When this is called, if the widget has any changed data to save it should call - * IWidgetManager.setData (id, data) to set it and return true. Otherwise it should return - * false. - * - * If the widget has subwidgets, it should also be recursively called on these (passing their - * ids). */ - bool saveChanges (); - /+ Use when widget editing is available? Requires widgets to know their parents. /** Called when a child widget's size has changed. *