changeset 122:f96e8d18c00a

Missed file from last commit.
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 02 Jan 2009 18:10:14 +0000
parents 5b37d0400732
children d3b2cefd46c9
files mde/gui/WMScreen.d
diffstat 1 files changed, 228 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mde/gui/WMScreen.d	Fri Jan 02 18:10:14 2009 +0000
@@ -0,0 +1,228 @@
+/* LICENSE BLOCK
+Part of mde: a Modular D game-oriented Engine
+Copyright © 2007-2008 Diggory Hardy
+
+This program is free software: you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation, either
+version 2 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+/*************************************************************************************************
+ * A gui manager class using mde.setup.Screen and mde.input.Input.
+ *
+ * This is the module to use externally to create a graphical user interface (likely also with
+ * content modules).
+ *************************************************************************************************/
+module mde.gui.WMScreen;
+
+import mde.gui.WidgetManager;
+import mde.gui.widget.Ifaces;
+import mde.gui.renderer.createRenderer;
+
+import mde.setup.Screen;
+import Items = mde.content.Items;	// loadTranslation
+import mde.lookup.Options;	// miscOpts.L10n callback
+
+import tango.util.log.Log : Log, Logger;
+
+private Logger logger;
+static this () {
+    logger = Log.getLogger ("mde.gui.WMScreen");
+}
+
+/*************************************************************************************************
+ * The widget manager.
+ * 
+ * This provides a layer on top of WidgetLoader, handling input and rendering. Other functionality
+ * is contained in the super class, to simplify supporting new input/graphics libraries.
+ * 
+ * Currently mouse coordinates are passed to widgets untranslated. It may make sense to translate
+ * them and possibly drop events for some uses, such as if the gui is drawn to a texture.
+ * 
+ * Aside from the IWidgetManager methods, this class should be thread-safe.
+ *************************************************************************************************/
+scope class WMScreen : AWidgetManager, Screen.IDrawable {
+    /** Construct a new widget manager.
+     * 
+     * params:
+     *  fileName = Name of file specifying the gui, excluding path and extension.
+     */
+    this (char[] file) {
+        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.
+    void init () {
+        // Doesn't need a lock - cannot conflict with other class functions.
+        // Events we want to know about:
+        imde.input.addMouseClickCallback(&clickEvent);
+        imde.input.addMouseMotionCallback(&motionEvent);
+	
+	Items.loadTranslation ();
+	miscOpts.L10n.addCallback (&reloadStrings);
+    }
+    
+    
+    /** Draw the gui. */
+    void draw() {
+        synchronized(mutex) {
+            if (child)
+                child.draw;
+	    foreach (popup; popups)
+		popup.widget.draw();
+	}
+    }
+    
+    /** For mouse click events.
+     *
+     * Sends the event on to the relevant windows and all click callbacks. */
+    void clickEvent (ushort usx, ushort usy, ubyte b, bool state) {
+        debug scope (failure)
+            logger.warn ("clickEvent: failed!");
+        mutex.lock;
+        scope(exit) mutex.unlock;
+        if (child is null) return;
+        
+        wdabs cx = cast(wdabs) usx, cy = cast(wdabs) usy;
+        
+        // 1. Callbacks have the highest priority recieving events (e.g. a button release)
+        foreach (dg; clickCallbacks)
+            if (dg (cx, cy, b, state)) return;
+        
+        // 2. Then pop-ups: close from top, depending on click pos
+        // Note: assumes each evaluated popup's parent is not under another still open popup.
+        // Also assumes popup's parent doesn't have other children in its box.
+        size_t removeTo = popups.length;
+        bool eventDone;		// don't pass clickEvent
+        IChildWidget widg;	// widget clicked on
+        foreach_reverse (i,popup; popups) with (popup) {
+            if (cx < x || cx >= x + w ||
+                cy < y || cy >= y + h) {	// on popup
+                if (parent.onSelf (cx, cy)) {
+                    if (parent.popupParentClick()) removeTo = i;
+                    eventDone = true;
+                    break;
+                } else {
+                    removeTo = i;
+                    parent.popupClose;
+                }
+            } else {
+                widg = widget.getWidget (cast(wdabs)cx,cast(wdabs)cy);
+                break;
+            }
+        }
+        if (removeTo < popups.length) {
+            requestRedraw;
+            popups = popups[0..removeTo];
+        }
+        if (eventDone)
+            return;
+        
+        // 3. Then the main widget tree
+        debug assert (cx < child.width && cy < child.height, "WidgetManager: child doesn't cover whole area (code error)");
+        if (widg is null)
+            widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy);
+	if (keyFocus && keyFocus !is widg) {
+	    keyFocus.keyFocusLost;
+	    keyFocus = null;
+	    imde.input.setLetterCallback (null);
+	}
+        if (widg !is null) {
+	    if (widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) {
+		keyFocus = widg;
+		imde.input.setLetterCallback (&widg.keyEvent);
+	    }
+	}
+    }
+    
+    /** For mouse motion events.
+     *
+     * Sends the event on to all motion callbacks. */
+    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 (cx, cy);
+	
+	IChildWidget ohighlighted = highlighted;
+	foreach_reverse (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;
+	}
+    }
+    
+    
+    void sizeEvent (int nw, int nh) {   // IDrawable function
+        mutex.lock;
+        scope(exit) mutex.unlock;
+        
+        w = cast(wdim) nw;
+        h = cast(wdim) nh;
+        
+        if (w < mw || h < mh)
+            logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h);
+        
+        if (!child) return;     // if not created yet.
+        child.setWidth  (w, -1);
+        child.setHeight (h, -1);
+        child.setPosition (0,0);
+    }
+    
+protected:
+    /* 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);
+	popups = null;
+        
+        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 || h < mh)
+            logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h);
+        
+        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;
+	    keyFocus = null;
+	    imde.input.setLetterCallback (null);
+	}
+    }
+}