changeset 132:264028f4115a

Cleaned up mde.imde and a couple of widget functions. New mde.menus module to add default menus. The input singleton is now created in mde.input.Input instead of mde.imde.
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 23 Jan 2009 14:59:05 +0000
parents 9cff74f68b84
children 9fd705793568
files data/L10n/en-GB.mtt data/conf/guiDemo.mtt examples/guiDemo.d mde/content/Items.d mde/events.d mde/gui/WMScreen.d mde/gui/WidgetManager.d mde/gui/widget/contentFunctions.d mde/imde.d mde/input/Input.d mde/menus.d mde/setup/Init.d mde/setup/InitStage.d
diffstat 13 files changed, 134 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- a/data/L10n/en-GB.mtt	Wed Jan 21 13:01:40 2009 +0000
+++ b/data/L10n/en-GB.mtt	Fri Jan 23 14:59:05 2009 +0000
@@ -1,8 +1,9 @@
 {MT01}
 !{en-GB British English}
 {imde}
-<entry|menu={0:"MDE",1:"MDE interface menu"}>
+<entry|main={0:"File",1:"Main MDE menu"}>
 <entry|quit={0:"Quit"}>
+<entry|debug={0:"Debug",1:"Debug-mode menu of debugging functions"}>
 {Options}
 <entry|Options={0:"Options"}>
 {FontOptions}
--- a/data/conf/guiDemo.mtt	Wed Jan 21 13:01:40 2009 +0000
+++ b/data/conf/guiDemo.mtt	Fri Jan 23 14:59:05 2009 +0000
@@ -2,16 +2,18 @@
 <char[]|Renderer="Simple">
 <char[]|Design="Working">
 {Working}
-<WidgetData|root={0:[0x4100,0,3,1],1:["bar","float","bar"]}>
+<WidgetData|root={0:[0x4100,0,2,1],1:["bar","float"]}>
 <WidgetData|float={0:[0x4200,14],1:["options"]}>
-<WidgetData|bar={0:[0x4100,14,1,3],1:["menu","blank","menu"]}>
+<WidgetData|bar={0:[0x4100,14,1,2],1:["menuContent","blank"]}>
 <WidgetData|blank={0:[0x2]}>
 
-<WidgetData|menu={0:[0x2031],1:["imde.menu","menu0"]}>
-<WidgetData|menu0={0:[0x4011,0],1:["menu1"]}>
-<WidgetData|menu1={0:[0xE032,0],1:["menu2"]}>
-<WidgetData|menu2={0:[0x6033],1:["menu3"]}>
-<WidgetData|menu3={0:[0xE032,0],1:["menu3"]}>
+<WidgetData|menuContent={0:[0x2031],1:["imde.menus","menus"]}>
+<WidgetData|menus={0:[0xC110,8]    ,1:["menuPopup"]}>
+<WidgetData|menuPopup={0:[0x6033,0],1:["menuList"]}>
+!{Could recurse to menuPopup if it wasn't for recursion protection:}
+<WidgetData|menuList={0:[0xE030,0] ,1:["menu1Popup"]}>
+<WidgetData|menu1Popup={0:[0x6033] ,1:["menu1List"]}>
+<WidgetData|menu1List={0:[0xE030,0],1:["blank"]}>
 
 <EnumContent|switch={0:["misc","video","font"],1:0}>
 <WidgetData|options={0:[0x2031],1:["dynamic.switch","switchL"]}>
--- a/examples/guiDemo.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/examples/guiDemo.d	Fri Jan 23 14:59:05 2009 +0000
@@ -17,6 +17,7 @@
 module examples.guiDemo;
 
 import mde.imde;                        // this module's interface for external modules
+import mde.menus;		// add default menus
 import mde.events;      // pollEvents() // NOTE: Must be imported before Init, otherwise fonts don't display properly (why??)
 import mde.setup.Init;                  // initialization
 import mde.lookup.Options;              // pollInterval option
--- a/mde/content/Items.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/content/Items.d	Fri Jan 23 14:59:05 2009 +0000
@@ -60,10 +60,8 @@
 	    }
 	} else if (h == "imde") {
 	    h = head (item);
-	    if (h == "menu" && item is null)
-		return imde.menu;
-	    else if (h == "quit" && item is null)
-		return imde.quit;
+	    if (h == "menus" && item is null)
+		return imde.menus;
 	} else if (h == "dynamic") {
             auto i = head (item) in items;
             if (i) return *i;
@@ -117,10 +115,8 @@
 	
 	// Translate imde:
         trl = Translation.get ("imde");
-	trle = trl.getStruct ("menu");
-	imde.menu.name (trle.name, trle.desc);
-	trle = trl.getStruct ("quit");
-	imde.quit.name (trle.name, trle.desc);
+	trle = trl.getStruct ("menus");
+	imde.menus.name (trle.name, trle.desc);
         
         // Translate dynamic content:
         if (items.length) {
--- a/mde/events.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/events.d	Fri Jan 23 14:59:05 2009 +0000
@@ -28,8 +28,10 @@
 import tango.util.log.Log : Log, Logger;
 
 private Logger logger;
+private Input input;
 static this() {
     logger = Log.getLogger ("mde.events");
+    input = Input.singleton;
 }
 
 void pollEvents (TimeSpan) {
@@ -37,7 +39,7 @@
     while (SDL_PollEvent (&event)) {
         switch (event.type) {
             case SDL_QUIT:
-                logger.info ("Quit requested");
+                debug logger.trace ("Quit (from SDL_QUIT event)");
                 imde.run = false;
                 break;
             case SDL_VIDEORESIZE:
@@ -50,8 +52,8 @@
                 break;
             default:
                 try {
-                    if (!imde.input (event))
-                        logger.warn ("Unrecognised event with code {}", event.type);
+                    if (!input.send (event))
+                        logger.info ("Unrecognised event with code {}", event.type);
                 } catch (Exception e) {
                     logger.error ("Caught input exception; event will be ignored. Exception was:");
                     logger.error (e.msg);
--- a/mde/gui/WMScreen.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/gui/WMScreen.d	Fri Jan 23 14:59:05 2009 +0000
@@ -26,6 +26,7 @@
 import mde.gui.renderer.createRenderer;
 
 import mde.setup.Screen;
+import mde.input.Input;
 
 import tango.util.log.Log : Log, Logger;
 
@@ -61,8 +62,9 @@
         
         Screen.addDrawable (this);
         // Events we want to know about:
-        imde.input.addMouseClickCallback(&clickEvent);
-        imde.input.addMouseMotionCallback(&motionEvent);
+        input = Input.singleton;
+        input.addMouseClickCallback(&clickEvent)
+             .addMouseMotionCallback(&motionEvent);
     }
     
     /** Draw the gui. */
@@ -98,13 +100,13 @@
 	if (keyFocus && keyFocus !is underMouse) {
 	    keyFocus.keyFocusLost;
 	    keyFocus = null;
-	    imde.input.setLetterCallback (null);
+            input.setLetterCallback (null);
 	}
         // Finally, post the actual event:
         if (underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) {
             // keyboard input requested
             keyFocus = underMouse;
-            imde.input.setLetterCallback (&underMouse.keyEvent);
+            input.setLetterCallback (&underMouse.keyEvent);
         }
     }
     
@@ -181,7 +183,9 @@
 	if (keyFocus) {
 	    keyFocus.keyFocusLost;
 	    keyFocus = null;
-	    imde.input.setLetterCallback (null);
+            input.setLetterCallback (null);
 	}
     }
+    
+    Input input;	// input singleton
 }
--- a/mde/gui/WidgetManager.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/gui/WidgetManager.d	Fri Jan 23 14:59:05 2009 +0000
@@ -34,6 +34,7 @@
 import Items = mde.content.Items;	// loadTranslation
 debug import mde.content.miscContent;	// Debug menu
 
+import mt = mde.file.mergetag.DataSet;
 import mde.file.mergetag.Reader;
 import mde.file.mergetag.Writer;
 import mde.setup.paths;
@@ -80,7 +81,7 @@
         debug {
             auto lWS = new EventContent ("logWidgetSize");
             lWS.addCallback (&logWidgetSize);
-            imde.menu.append (lWS);
+            imde.menus.append (new ContentList ("debug", [cast(Content)lWS]));
         }
     }
     
@@ -492,8 +493,7 @@
     // content functions: 0x30
     editContent		= FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x30,
     addContent		= FUNCTION | 0x31,
-    flatMenuContent	= FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x32,
-    subMenuContent	= FUNCTION | TAKES_CONTENT | 0x33,
+    popupListContent	= FUNCTION | TAKES_CONTENT | 0x33,
     
     // content widgets: 0x40
     DisplayContent	= TAKES_CONTENT | 0x40,
@@ -524,10 +524,9 @@
 	"GridLayout",
 	"FloatingArea",
 	"Switch",
-	"subMenuContent",
+	"popupListContent",
 	"ContentList",
-	"editContent",
-	"flatMenuContent"];
+	"editContent"];
 
 /* Generates a binary search algorithm for makeWidget. */
 char[] binarySearch (char[] var, char[][] consts) {
--- a/mde/gui/widget/contentFunctions.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/gui/widget/contentFunctions.d	Fri Jan 23 14:59:05 2009 +0000
@@ -65,27 +65,13 @@
 }
 
 /******************************************************************************
- * A function which returns a ContentListWidget or MenuButtonContentWidget.
+ * PopupMenuContent will create a popup for any content. This function more
+ * conservatively only creates a popup for a ContentList, and otherwise returns
+ * the same as editContent.
  *****************************************************************************/
-IChildWidget flatMenuContent (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) {
-    if (c is null) throw new ContentException;
-    if (cast(IContentList) c)
-        return new ContentListWidget(mgr,parent,id,data,c);
-    if (cast(EventContent) c)
-        return new ButtonContentWidget(mgr,parent,id,data,c);
-    // generic uneditable option
-    return new DisplayContentWidget(mgr,parent,id,data,c);
-}
-
-/******************************************************************************
- * A function which returns a PopupMenuWidget or MenuButtonContentWidget.
- *****************************************************************************/
-IChildWidget subMenuContent (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) {
+IChildWidget popupListContent (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) {
     if (c is null) throw new ContentException;
     if (cast(IContentList) c)
         return new PopupMenuWidget(mgr,parent,id,data,c);
-    if (cast(EventContent) c)
-        return new ButtonContentWidget(mgr,parent,id,data,c);
-    // generic uneditable option
-    return new DisplayContentWidget(mgr,parent,id,data,c);
+    else return editContent (mgr, parent, id, data, c);
 }
--- a/mde/imde.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/imde.d	Fri Jan 23 14:59:05 2009 +0000
@@ -13,33 +13,22 @@
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-/** This module is for interfacing with the mde.mde module (or other module containing main()) and
- * some global items. */
+/******************************************************************************
+ *  This module is for interfacing with the mde.mde module (or other module
+ *  containing main()) and contains some global references.
+ *****************************************************************************/
 module mde.imde;
 
-import mde.input.Input;
 import mde.scheduler.Scheduler;
 import mde.content.miscContent;
 
 static this () {
-    // Make these available to all importing modules' static CTORs, as well as during init.
-    input = new Input();
+    // Make available to all importing modules:
     mainSchedule = new Scheduler;
-    
-    quit = new EventContent("quit");
-    quit.addCallback ((Content){
-	run = false;
-    });
-    menu = new ContentList ("menu",[cast(Content) quit,
-                            new EventContent("a"),
-                            new ContentList("subMenu",[
-                                           new EventContent("b"),
-                                           new EventContent("c")])
-                            ]);
+    menus = new ContentList ("menu");
 }
 
-ContentList menu;	/// Root menu for imde
-EventContent quit;	/// A content triggering mde to halt
+ContentList menus;	/// Root of all menus; import mde.menus to add entries
 
 Scheduler mainSchedule; /// The schedule used by the main loop.
 
@@ -49,5 +38,3 @@
 };
 
 bool run = true;	// main loop continues if this is true
-
-Input input;		// Input instance. When multiple users are allowed instances will be per-user.
--- a/mde/input/Input.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/input/Input.d	Fri Jan 23 14:59:05 2009 +0000
@@ -143,15 +143,17 @@
     /** Adds a callback delegate for key events (both DOWN and UP) with this ID.
     *
     * Delegate receives event status. */
-    void addButtonCallback (inputID id, ButtonCallback dg) {
+    Input addButtonCallback (inputID id, ButtonCallback dg) {
         buttonCallbacks[id] ~= dg;
+        return this;
     }
 
     /** Adds a callback delegate for axis events with this ID.
     *
     * Delegate receives event status (as per what getAxis returns). */
-    void addAxisCallback (inputID id, AxisCallback dg) {
+    Input addAxisCallback (inputID id, AxisCallback dg) {
         axisCallbacks[id] ~= dg;
+        return this;
     }
 
     /** Adds a callback delegate for mouse motion/joystick ball events with this ID.
@@ -162,8 +164,9 @@
     * (A separate callback for mouse screen position changes is not
     * necessary since this will be triggered by the same event - use mouseScreenPos from within the
     * function to get new screen coordinates.) */
-    void addRelMotionCallback (inputID id, RelMotionCallback dg) {
+    Input addRelMotionCallback (inputID id, RelMotionCallback dg) {
         relMotionCallbacks[id] ~= dg;
+        return this;
     }
     
     /** Adds a callback delegate for all mouse clicks & releases.
@@ -175,16 +178,18 @@
     * The point of this over a standard button callback is firstly to avoid mouse configuration for
     * the GUI, and secondly to give the pointer position at the time of the event, not the time the
     * callback gets called. */
-    void addMouseClickCallback (MouseClickCallback dg) {
+    Input addMouseClickCallback (MouseClickCallback dg) {
         mouseClickCallbacks ~= dg;
+        return this;
     }
     
     /** Adds a callback delegate for all mouse motion events.
     *
     * Really just for graphical user interfaces. Use addRelMotionCallback for relative motion (for
     * manipulating 3D views, etc.). */
-    void addMouseMotionCallback (MouseMotionCallback dg) {
+    Input addMouseMotionCallback (MouseMotionCallback dg) {
         mouseMotionCallbacks ~= dg;
+        return this;
     }
     
     /** Sets a callback delegate to recieve key presses as a Utf-8 char[].
@@ -206,21 +211,22 @@
     }
 
     /** Feed an SDL_Event struct (only uses if it's a key, mouse or joystick event).
-    *
-    * Other types of event functions may be added. Returns true if the event was used, false if not
-    * or no config was available. Hmm... doesn't seem very useful, but has practically no cost.
-    * (Due to lack of use of this feature, false is returned even for used events when no config is
-    * available).
-    *
-    * May throw InputClassExceptions (on configuration errors). Catching the exception and continuing should
-    * be fine. */
-    bool opCall (ref SDL_Event event) {
+     *
+     * Other types of event functions may be added. Returns true if the event
+     * was used, false if not or no config was available. Hmm... doesn't seem
+     * very useful, but has practically no cost.
+     *
+     * May throw InputClassExceptions (on configuration errors). Catching the
+     * exception and continuing should be fine. */
+    bool send (ref SDL_Event event) {
         /* Non-config events.
         *
-        * Handle these first so that if no config exists some functionality at least is retained.
+        * Handle these first so that if no config exists some functionality at
+        * least is retained.
         *
         * Coordinates don't need adjusting (they put the top-left most pixel at 0,0).
-        */
+        * 
+        * If no config, exit from this switch. */
         switch (event.type) {
             case SDL_KEYDOWN:
                 if (letterCallback) {
@@ -252,12 +258,9 @@
                 break;
             
             default:
+                if (!config) return false;	// event not used
         }
-        
-        /* No config available, so don't try to access it and segfault.
-        * Don't log a message because this function is called per-event (i.e. frequently).
-        * A message should already have been logged by loadConfig anyway. */
-        if (!config) return false;
+        if (!config) return true;		// event used
         
         switch (event.type) {
             // Keyboard events:
@@ -388,11 +391,19 @@
         Config* c_p = profile in Config.configs;
         if (c_p) config = *c_p;
         else {
+            logger.error ("Config profile \""~profile~"\" not found: input won't work unless a valid profile is loaded!");
             throw new ConfigLoadException;
-            logger.error ("Config profile \""~profile~"\" not found: input won't work unless a valid profile is loaded!");
         }
     }
     
+    /** For now, use as a singleton. (Could be changed later to allow multiple
+     * "players". */
+    static Input singleton () {
+        if (instance is null)
+            instance = new Input();
+        return instance;
+    }
+    
 private:
     // Static constructor for event stream (fills es_*_fcts tables).
     static this () {
@@ -414,6 +425,7 @@
     
     static const CB_EXC = "Callback exception: ";
     
+    static Input instance;
     static Logger logger;
 
     Config config;		// Configuration
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mde/menus.d	Fri Jan 23 14:59:05 2009 +0000
@@ -0,0 +1,40 @@
+/* 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/>. */
+
+/******************************************************************************
+ * Import this module to add some standard menus to imde.menus.
+ * 
+ * Note that the widget manager also adds menu items in debug mode.
+ *****************************************************************************/
+module mde.menus;
+
+import mde.content.miscContent;
+import mde.imde;
+debug {
+    import tango.util.log.Log : Log, Logger;
+    private Logger logger;
+    static this () {
+        logger = Log.getLogger ("mde.menus");
+    }
+}
+
+static this () {
+    auto quit = new EventContent("quit");
+    quit.addCallback ((Content){
+        debug logger.trace ("Quit (from menu)");
+        run = false;
+    });
+    menus.append (new ContentList ("main", [cast(Content)quit]));
+}
--- a/mde/setup/Init.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/setup/Init.d	Fri Jan 23 14:59:05 2009 +0000
@@ -329,19 +329,21 @@
                     try {
                         static if (startup) {
                             debug logger.trace ("({}) InitStage {}: starting init", threadNum, stage.name);
-                            stage.state = (*stage).init();  // init is a property of a pointer (oh no!)
+                            stage.state = (*stage).init();  // init is a property too :-(
                         } else {
                             debug logger.trace ("({}) InitStage {}: starting cleanup", threadNum, stage.name);
                             stage.state = stage.cleanup();
                         }
                         debug logger.trace ("({}) InitStage {}: completed; state: {}", threadNum, stage.name, stage.state);
                     } catch (InitStageException e) {
-                        debug logger.trace ("({}) InitStage {}: failed: "~e.msg, threadNum, stage.name);
+                        debug logger.error ("({}) InitStage {}: failed: "~e.msg, threadNum, stage.name);
+                        else logger.error ("InitStage {}: failed: "~e.msg, stage.name);
                         stage.state = e.state;
                         doneInit = STATE.ABORT;
                         break threadLoop;
                     } catch (Exception e) {
-                        debug logger.trace ("({}) InitStage {}: failed: "~e.msg, threadNum, stage.name);
+                        debug logger.error ("({}) InitStage {}: failed: "~e.msg, threadNum, stage.name);
+                        else logger.error ("InitStage {}: failed: "~e.msg, stage.name);
                         doneInit = STATE.ABORT;
                         break threadLoop;
                     }
--- a/mde/setup/InitStage.d	Wed Jan 21 13:01:40 2009 +0000
+++ b/mde/setup/InitStage.d	Fri Jan 23 14:59:05 2009 +0000
@@ -13,9 +13,9 @@
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-/**************************************************************************************************
+/******************************************************************************
  * The infrastructure for handling external startup/shutdown code.
- *************************************************************************************************/
+ *****************************************************************************/
 module mde.setup.InitStage;
 
 public import mde.setup.exception;
@@ -81,9 +81,9 @@
 
 
 
-/**************************************************************************************************
+/******************************************************************************
  * Initialization functions.
- *************************************************************************************************/
+ *****************************************************************************/
 import imde = mde.imde;
 import mde.input.Input;
 import mde.setup.Screen;
@@ -99,12 +99,12 @@
 }
 
 StageState initInput () {
-    imde.input.loadConfig ("input");
+    Input.singleton.loadConfig ("input");
     
-    // Quit on escape. NOTE: quit via SDL_QUIT event is handled completely independently!
-    imde.input.addButtonCallback (cast(Input.inputID) 0x0u, delegate void(Input.inputID i, bool b) {
+    // Quit on escape.
+    Input.singleton.addButtonCallback (cast(Input.inputID) 0x0u, delegate void(Input.inputID i, bool b) {
         if (b) {
-            logger.info ("Quiting...");
+            debug logger.trace ("Quit (from Esc)");
             imde.run = false;
         }
     } );