diff mde/gui/WidgetManager.d @ 171:7f7b2011b759

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).
author Diggory Hardy <diggory.hardy@gmail.com>
date Sun, 26 Jul 2009 11:04:17 +0200
parents e45226d3deae
children a1ba9157510e
line wrap: on
line diff
--- 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.
 }