changeset 128:41582439a42b

Added support for dynamic EnumContent loading and saving, with translation loading. WMScreen.init removed; code moved to this() since class is now created by main() instead of a static this(). Fix for SwitchWidget not passing events. Still some resizing bugs evident in SwitchWidget :-(
author Diggory Hardy <diggory.hardy@gmail.com>
date Wed, 14 Jan 2009 20:24:14 +0000
parents 3328c6fb77ca
children ad91de8867a0
files data/L10n/en-GB.mtt data/conf/guiDemo.mtt examples/guiDemo.d mde/content/AStringContent.d mde/content/Items.d mde/content/miscContent.d mde/gui/WMScreen.d mde/gui/WidgetDataSet.d mde/gui/WidgetManager.d mde/gui/widget/miscContent.d mde/imde.d mde/lookup/Translation.d
diffstat 12 files changed, 122 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/data/L10n/en-GB.mtt	Thu Jan 08 20:09:46 2009 +0000
+++ b/data/L10n/en-GB.mtt	Wed Jan 14 20:24:14 2009 +0000
@@ -9,6 +9,8 @@
 <entry|FontOptions={0:"Font options"}>
 <entry|lcdFilter={0:"LCD filtering",1:"Enable or disable sub-pixel rendering. Note that the FreeType library may be compiled without support due to patent issues."}>
 <entry|renderMode={0:"Font rendering mode",1:"Controls how fonts are rendered: in gray-scale, or for LCDs (with a horizontal (usual) or vertical layout, with an RGB (usual) or BGR sub-pixel mode."}>
+<entry|defaultFont={0:"Default font",1:"Filename of default font"}>
+<entry|defaultSize={0:"Default size",1:"Size for default font"}>
 {MiscOptions}
 <entry|MiscOptions={0:"Miscellaneous options"}>
 <entry|maxThreads={0:"Max threads",1:"Maximum number of threads to use in mde (currently only applies to init stages run in parallel)."}>
@@ -33,3 +35,8 @@
 <entry|screenH={0:"Screen height",1:"Vertical resolution (fullscreen mode)."}>
 <entry|windowW={0:"Window width",1:"Horizontal size (windowed mode)."}>
 <entry|windowH={0:"Window height",1:"Vertical size (windowed mode)."}>
+{dynamic}
+<entry|switch={0:"Options"}>
+<entry|switch.misc={0:"Miscellaneous"}>
+<entry|switch.video={0:"Video"}>
+<entry|switch.font={0:"Font"}>
--- a/data/conf/guiDemo.mtt	Thu Jan 08 20:09:46 2009 +0000
+++ b/data/conf/guiDemo.mtt	Wed Jan 14 20:24:14 2009 +0000
@@ -3,20 +3,28 @@
 <char[]|Design="Working">
 {Working}
 <WidgetData|root={0:[0x4100,0,3,1],1:["bar","float","bar"]}>
-<WidgetData|float={0:[0x4200,14,14,14],1:["optaC","optaC","switchC"]}>
+<WidgetData|float={0:[0x4200,14],1:["options"]}>
 <WidgetData|bar={0:[0x4100,14,1,3],1:["menu","blank","menu"]}>
+<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|blank={0:[0x2]}>
-<WidgetData|optaC={0:[0x2031],1:["Options","opts"]}>
-<WidgetData|opts={0:[0x4100,4,2,1],1:["optName","optSecs"]}>
-<WidgetData|optSecs={0:[0xC110,4],1:["optSec"]}>
-<WidgetData|optSec={0:[0x4100,0,2,1],1:["optName","optVars"]}>
+
+<EnumContent|switch={0:["misc","video","font"],1:0}>
+<WidgetData|options={0:[0x2031],1:["dynamic.switch","switchL"]}>
+<WidgetData|switchL={0:[0x4100,4,2,1],1:["switchVal","switchT"]}>
+<WidgetData|switchVal={0:[0x4100,4,1,2],1:["optName","optVal"]}>
+<WidgetData|switchT={0:[0x4210],1:["optMisc","optVideo","optFont"]}>
+
+<WidgetData|optMisc={0:[0x2031],1:["Options.MiscOptions","optSec"]}>
+<WidgetData|optVideo={0:[0x2031],1:["Options.VideoOptions","optSec"]}>
+<WidgetData|optFont={0:[0x2031],1:["Options.FontOptions","optSec"]}>
+
 !{use optBox for no description, optDBox for descriptions under entries}
-<WidgetData|optVars={0:[0xC110,0],1:["optBox"]}>
+<WidgetData|optSec={0:[0xC110,0],1:["optBox"]}>
 <WidgetData|optDBox={0:[0x4100,1,2,1],1:["optBox","optDesc"]}>
 <WidgetData|optBox={0:[0x4100,1,1,3],1:["optName","optSep","optVal"]}>
 <WidgetData|optName={0:[0x4020, 1, 0xffffff]}>
@@ -24,10 +32,5 @@
 <WidgetData|optVal={0:[0xE030,12],1:["optEnum"]}>
 <WidgetData|optEnum={0:[0x4100,0,1,2],1:["optVal","optName"]}>
 <WidgetData|optSep={0:[0x21, 0xff],1:[" = "]}>
-<WidgetData|switchC={0:[0x2031],1:["imde.sw","switchL"]}>
-<WidgetData|switchL={0:[0x4100,0,2,1],1:["optVal","switchT"]}>
-<WidgetData|switchT={0:[0x4210],1:["1","2"]}>
-<WidgetData|1={0:[0x21,0xcf],1:["one"]}>
-<WidgetData|2={0:[0x21,0xcf],1:["two  T  W  O"]}>
 {Basic}
 <WidgetData|root={0:[0x21,0x90D970],1:["A string!"]}>
--- a/examples/guiDemo.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/examples/guiDemo.d	Wed Jan 14 20:24:14 2009 +0000
@@ -45,7 +45,6 @@
     // Set up the gui
     scope WMScreen gui = new WMScreen ("guiDemo");
     StageState guiLoad () {   // init func
-        gui.init;
         gui.loadDesign();
         return StageState.ACTIVE;
     }
--- a/mde/content/AStringContent.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/content/AStringContent.d	Wed Jan 14 20:24:14 2009 +0000
@@ -302,6 +302,7 @@
     *	val = which value is active; must be in [0,length-1]
     */
     this (char[] symbol, char[][] enumSymbols, int val = 0) {
+        this.enumSymbols = enumSymbols;
         enums.length = enumSymbols.length;
         char[] symPeriod = symbol~'.';
         foreach (i, ref e; enums) {
@@ -310,6 +311,20 @@
         super (symbol, val);	// calls assignNoCb
     }
     
+    /** Create from a EnumCStruct. */
+    this (char[] symbol, EnumCStruct data) {
+        this (symbol, data.symbols, data.value);
+    }
+    
+    /** Return an EnumCStruct, which could be used to recreate this Content. */
+    //NOTE: save EnumCStruct directly?
+    EnumCStruct structOf () {
+        EnumCStruct r;
+        r.symbols = enumSymbols;
+        r.value = v;
+        return r;
+    }
+    
     override void assignNoCb (int val) {	// called by children (via opAssign)
         if (val >= enums.length || val < 0) {
 	    logger.error ("EnumContent "~name_~" assigned invalid value; keeping value: "~sv);
@@ -351,8 +366,14 @@
         return enums;
     }
     
+    struct EnumCStruct {
+        char[][] symbols;
+        int value;
+    }
+    
 protected:
     EnumValueContent[] enums;
+    char[][] enumSymbols;	// saved for getStructOf
     
     /** Special version of BoolContent for each enumeration to update the parent Enum. */
     private class EnumValueContent : BoolContent {
--- a/mde/content/Items.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/content/Items.d	Wed Jan 14 20:24:14 2009 +0000
@@ -41,7 +41,8 @@
      * Items.get ("Options.MiscOptions") returns a ContentList of all misc options. */
     Content get (char[] item) {
 	assert (currentL10n is miscOpts.L10n(), "must call loadTranslation (code error)");
-	
+	char[] orig = item;	// item is modified by head()
+        
 	char[] h = head (item);
 	if (h == "Options") {
 	    if (item is null)
@@ -63,11 +64,12 @@
 		return imde.menu;
 	    else if (h == "quit" && item is null)
 		return imde.quit;
-            else if (h == "sw" && item is null)
-                return imde.sw;
-	}
-        logger.warn ("Bad content specifier: {}",h);
-	return new ErrorContent ("Error: bad content specifier",h);
+	} else if (h == "dynamic") {
+            auto i = head (item) in items;
+            if (i) return *i;
+        }
+        
+	return new ErrorContent ("Error: bad content specifier", orig);
     }
     
     /** Creates some content on first run (required by get()).
@@ -88,14 +90,15 @@
 	    Options.allContentList = new ContentList ("Options", list);
 	}
 	
+        Translation trl;
+        Translation.Entry trle;
+        
 	// Translate Options:
-	Translation.Entry trle;
 	with (Options.allContentList) {
 	    trle = Translation.get (symbol).getStruct (symbol);
 	    name (trle.name, trle.desc);
 	}
 	foreach (n,opts; Options.optionsClasses) {
-	    Translation trl;
 	    trl = Translation.get (n);
 	    trle = trl.getStruct (n);
 	    opts.contentList.name (trle.name, trle.desc);
@@ -113,15 +116,42 @@
 	}
 	
 	// Translate imde:
-	trle = Translation.get ("imde").getStruct ("menu");
+        trl = Translation.get ("imde");
+	trle = trl.getStruct ("menu");
 	imde.menu.name (trle.name, trle.desc);
-	trle = Translation.get ("imde").getStruct ("quit");
+	trle = trl.getStruct ("quit");
 	imde.quit.name (trle.name, trle.desc);
+        
+        // Translate dynamic content:
+        if (items.length) {
+            trl = Translation.get ("dynamic");
+            foreach (n,item; items) {
+                trle = trl.getStruct (n);
+                item.name (trle.name, trle.desc);
+                IContentList cl = cast(IContentList) item;
+                if (cl) {
+                    foreach (i,c; cl.list) {
+                        trle = trl.getStruct (c.symbol);
+                        c.name (trle.name, trle.desc);
+                    }
+                }
+            }
+        }
 	
 	currentL10n = miscOpts.L10n();
     }
     
+    /** Add content c with name c.symbol, so that translations are loaded for it and it can be
+     * returned by get ("dynamic."~c.symbol). */
+    Content addContent (Content c) {
+        items[c.symbol] = c;
+        return c;
+    }
+    
 private:
+    // NOTE: possibly add all content to this list. Lookups would be faster.
+    Content[char[]] items;	// dynamically added content
+    
     /** Takes the string "head.tail" where tail may contain '.' but head does not, returns "head",
      * with str set to "tail". */
     char[] head (ref char[] str) {
--- a/mde/content/miscContent.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/content/miscContent.d	Wed Jan 14 20:24:14 2009 +0000
@@ -57,6 +57,7 @@
     this (char[] name, char[] msg) {
 	super (name);
         this.msg = msg;
+        logger.error (name ~ ": " ~ msg);
     }
     
     override char[] toString (uint i) {
--- a/mde/gui/WMScreen.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/gui/WMScreen.d	Wed Jan 14 20:24:14 2009 +0000
@@ -26,8 +26,6 @@
 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;
 
@@ -50,29 +48,21 @@
 scope class WMScreen : AWidgetManager, Screen.IDrawable {
     /** Construct a new widget manager.
      * 
+     * Must be run after static this.
+     * 
      * params:
      *  fileName = Name of file specifying the gui, excluding path and extension.
      */
     this (char[] file) {
+        // Doesn't need a lock - cannot conflict with other class functions.
         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) {
--- a/mde/gui/WidgetDataSet.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/gui/WidgetDataSet.d	Wed Jan 14 20:24:14 2009 +0000
@@ -28,11 +28,14 @@
 module mde.gui.WidgetDataSet;
 
 public import mde.gui.types;
+import mde.content.AStringContent;
+import Items = mde.content.Items;
 
 // For loading from file:
 import mt = mde.file.mergetag.DataSet;
 import mt = mde.file.mergetag.DefaultData;
 import mde.file.serialize;
+
 import tango.util.log.Log : Log, Logger;
 
 private Logger logger;
@@ -52,6 +55,11 @@
             widgetData[id] = deserialize!(WidgetData) (dt);
         } else if (tp == "WDims" && (id in dimData) is null) {
             dimData[id] = cast(wdims) deserialize!(int[]) (dt);
+        } else if (tp == "EnumContent" && (id in enumContent) is null) {
+            // Add dynamic content. NOTE: could confict with content from another design/section.
+            EnumContent a = new EnumContent (id, deserialize!(EnumContent.EnumCStruct) (dt));
+            enumContent[id] = a;
+            Items.addContent (a);
         }
     }
     // Only WidgetDataChanges is used for writing.
@@ -77,6 +85,7 @@
 protected:
     WidgetData[widgetID] widgetData;    // Per-widget data
     wdims[widgetID] dimData;            // Per-widget sizes
+    EnumContent[char[]] enumContent;
 }
 
 /*************************************************************************************************
@@ -88,7 +97,9 @@
 {
     /**
      * Params:
-     *  wds = The base WidgetDataSet these changes are applied against. */
+     *  wds = The base WidgetDataSet these changes are applied against.
+     *  
+     * Base's enumContent is used directly; this.enumContent is null. */
     this (WidgetDataSet wds) {
         base = wds;
     }
@@ -101,6 +112,8 @@
             dlg ("WidgetData", id, serialize!(WidgetData) (data));
         foreach (id,dim; dimData)
             dlg ("WDims", id, serialize!(int[]) (cast(int[]) dim));
+        foreach (id,c; base.enumContent)
+            dlg ("EnumContent", id, serialize (c.structOf));
     }
     //END Mergetag code
     
@@ -118,8 +131,8 @@
     }
     
     /** Do any changes exist? True if no changes have been stored. */
-    bool none () {
-        return widgetData.length == 0;
+    bool noChanges () {
+        return widgetData.length == 0 && dimData.length == 0 && enumContent.length == 0;
     }
     
     protected WidgetDataSet base;
--- a/mde/gui/WidgetManager.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/gui/WidgetManager.d	Wed Jan 14 20:24:14 2009 +0000
@@ -24,9 +24,12 @@
 import mde.gui.WidgetDataSet;
 import mde.gui.widget.Ifaces;
 import mde.gui.exception;
-import mde.content.Content;
 
 import imde = mde.imde;
+import mde.lookup.Options;	// miscOpts.L10n callback
+import mde.content.Content;
+import Items = mde.content.Items;	// loadTranslation
+
 import mde.file.mergetag.Reader;
 import mde.file.mergetag.Writer;
 import mde.setup.paths;
@@ -64,6 +67,10 @@
     protected this (char[] file) {
         mutex = new Mutex;  // Used on functions intended to be called from outside the gui package.
         fileName = file;
+        miscOpts.L10n.addCallback (&reloadStrings);
+        
+        clickCallbacks = new typeof(clickCallbacks);
+        motionCallbacks = new typeof(motionCallbacks);
     }
     
     /* Load the widgets' data from the file specified to the CTOR.
@@ -123,6 +130,8 @@
             logger.error (e.msg);
             throw new GuiException ("Failure parsing config file");
         }
+	
+        Items.loadTranslation ();
     }
     
     /** Load the gui from some design.
@@ -180,8 +189,10 @@
         mutex.lock;
         scope(exit) mutex.unlock;
         
-        // Make all widgets save any changed data; return if no changes:
-        if (!child.saveChanges)
+        // Make all widgets save any changed data:
+        child.saveChanges;
+        
+        if (changes.noChanges)
             return;
         
         if (loadUserFile) { // merge entries from user file into current changes
--- a/mde/gui/widget/miscContent.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/gui/widget/miscContent.d	Wed Jan 14 20:24:14 2009 +0000
@@ -198,6 +198,10 @@
         currentW.setPosition (nx,ny);
     }
     
+    override IChildWidget getWidget (wdim cx, wdim cy) {
+        return currentW.getWidget (cx, cy);
+    }
+    
     override void draw () {
         currentW.draw;
     }
--- a/mde/imde.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/imde.d	Wed Jan 14 20:24:14 2009 +0000
@@ -20,7 +20,6 @@
 import mde.input.Input;
 import mde.scheduler.Scheduler;
 import mde.content.miscContent;
-import mde.content.AStringContent;	//FIXME: for sw
 
 static this () {
     // Make these available to all importing modules' static CTORs, as well as during init.
@@ -37,15 +36,11 @@
                                            new EventContent("b"),
                                            new EventContent("c")])
                             ]);
-    
-    sw = new EnumContent ("switch", ["one", "two"]);
 }
 
 ContentList menu;	/// Root menu for imde
 EventContent quit;	/// A content triggering mde to halt
 
-EnumContent sw;
-
 Scheduler mainSchedule; /// The schedule used by the main loop.
 
 /** Some enums used by per request scheduled functions. */
--- a/mde/lookup/Translation.d	Thu Jan 08 20:09:46 2009 +0000
+++ b/mde/lookup/Translation.d	Wed Jan 14 20:24:14 2009 +0000
@@ -156,6 +156,7 @@
         if (p) {
             return *p;
         } else {
+            logger.warn ("Unable to find translation for {} in {}", id, section);
             Entry ret;
             ret.name = id;
             return ret;