changeset 92:085f2ca31914

Shared alignments supported in more complex cases.
author Diggory Hardy <diggory.hardy@gmail.com>
date Tue, 21 Oct 2008 09:57:19 +0100
parents 4d5d53e4f881
children 08a4ae11454b
files data/conf/gui.mtt data/conf/options.mtt mde/gui/WidgetManager.d mde/gui/widget/Floating.d mde/gui/widget/Ifaces.d mde/gui/widget/Widget.d mde/gui/widget/layout.d
diffstat 7 files changed, 130 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/data/conf/gui.mtt	Thu Oct 16 17:43:48 2008 +0100
+++ b/data/conf/gui.mtt	Tue Oct 21 09:57:19 2008 +0100
@@ -7,9 +7,11 @@
 <WidgetData|content={0:[0xC100,0,4,2],1:["floating","button","blank","blank","blank","opts","blank","blank"]}>
 <WidgetData|button={0:[0x10,50,50]}>
 <WidgetData|blank={0:[0x2]}>
-<WidgetData|opts={0:[0x8110,0],1:["optBox"]}>
+<WidgetData|opts={0:[0x8110,0],1:["optDBox"]}>
+<WidgetData|optDBox={0:[0xC100,0,2,1],1:["optBox","optDesc"]}>
 <WidgetData|optBox={0:[0xC100,1,1,3],1:["optName","optSep","optVal"]}>
 <WidgetData|optName={0:[0x4020, 1, 0xfe8c00]}>
+<WidgetData|optDesc={0:[0x4020, 2, 0xaf6000]}>
 <WidgetData|optVal={0:[0x4020, 0, 0xBF00]}>
 <WidgetData|optSep={0:[0x21, 0xff],1:["="]}>
 <WidgetData|floating={0:[0x8200,20,20],1:["text"]}>
--- a/data/conf/options.mtt	Thu Oct 16 17:43:48 2008 +0100
+++ b/data/conf/options.mtt	Tue Oct 21 09:57:19 2008 +0100
@@ -5,11 +5,6 @@
 <char[]|L10n="en-GB">
 <int|logOptions=0x3000>
 <double|pollInterval=0.01>
-<char[]|a="tildb\naeouc\ngpqyg">
-<char[]|b="tildb">
-<char[]|c="aeouc">
-<char[]|g="gpqy">
-<char[]|z="fghijklpq">
 
 {font}
 <int|lcdFilter=2>
--- a/mde/gui/WidgetManager.d	Thu Oct 16 17:43:48 2008 +0100
+++ b/mde/gui/WidgetManager.d	Tue Oct 21 09:57:19 2008 +0100
@@ -62,8 +62,7 @@
         Screen.addDrawable (this);
     }
     
-    // NOTE - temporarily here to allow CTOR to run safely during static this
-    // called during init
+    // 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:
@@ -170,6 +169,7 @@
             rend = createRenderer (rendName);
         
         child = makeWidget ("root");
+        finalize;
         
         mw = child.minWidth;
         mh = child.minHeight;
@@ -384,7 +384,7 @@
     /** Create a widget by ID.
      *
      * A widget instance is created from data found under ID. Multiple instances may be created.
-     * FIXME - data conflicts when saving?
+     * NOTE - data conflicts when saving?
      *
      * An IContent may be passed. This could contain many things, e.g. some basic data, a widget,
      * multiple sub-IContents. It is only passed to the widget by createWidget if it's enumeration
@@ -394,6 +394,37 @@
         return createWidget (this, id, curData[id], content);
     }
     
+    /** Runs finalize for all descendants, in a deepest first order. */
+    /* NOTE: The way this function works may seem a little odd, but it's designed to allow
+     * shared alignments to be initialized properly:
+     * 1. all sharing members need to know their children's min size
+     * 2. all sharing members need to add their children's min size to the alignment
+     * 3. all sharing members can only then get their min size
+     * This method will fail if alignment members are not all of the same generation. An alternate
+     * method without this drawback would be to have shared alignments created with a list of
+     * pointers to their members, and once all members have been created the alignment could
+     * initialize itself, first making sure each members' children have been initialized. */
+    void finalize () {
+        IChildWidget[][] descendants;   // first index: depth; is a list of widgets at each depth
+        
+        void recurseChildren (size_t depth, IChildWidget widget) {
+            foreach (child; widget.children)
+                recurseChildren (depth+1, child);
+            
+            if (descendants.length <= depth)
+                descendants.length = depth * 2 + 1;
+            descendants[depth] ~= widget;
+        }
+        
+        recurseChildren (0, child);
+        foreach_reverse (generation; descendants) {
+            foreach (widget; generation)
+                widget.prefinalize;
+            foreach (widget; generation)
+                widget.finalize;
+        }
+    }
+    
     /** For making changes. */
     void setData (widgetID id, WidgetData d) {
         changes[id] = d;        // also updates WidgetDataSet in data.
@@ -405,6 +436,7 @@
     * ---
     * // 1. Create the root widget:
     * child = makeWidget ("root");
+    * finalize;
     * // 2. Set the setSize, e.g.:
     * child.setWidth  (child.minWidth,  1);
     * child.setHeight (child.minHeight, 1);
--- a/mde/gui/widget/Floating.d	Thu Oct 16 17:43:48 2008 +0100
+++ b/mde/gui/widget/Floating.d	Tue Oct 21 09:57:19 2008 +0100
@@ -53,12 +53,14 @@
  * Ints supplied may consist of just the widget type or
  * additionally an (x,y) coordinate for each subwidget (all x coords first, then all y coords).
  */
-class FloatingAreaWidget : SizableWidget
+class FloatingAreaWidget : ParentWidget
 {
     this (IWidgetManager mgr, widgetID id, WidgetData data) {
         subWidgets.length = data.strings.length;
         foreach (i,s; data.strings)
             subWidgets[i] = mgr.makeWidget (s);
+        foreach (w; subWidgets)
+            w.finalize;
         sWCoords.length = subWidgets.length;
         
         if (data.ints.length != 1) {
@@ -80,6 +82,9 @@
         }
     }
     
+    bool isWSizable () {    return true;    }
+    bool isHSizable () {    return true;    }
+    
     void setPosition (wdim x, wdim y) {
         super.setPosition (x,y);
         
@@ -97,7 +102,6 @@
     }
     
 protected:
-    IChildWidget[] subWidgets;  // all subwidgets, framed or not
     wdimPair[] sWCoords;        // coords for subwidgets, relative to this widget
     
     /+
--- a/mde/gui/widget/Ifaces.d	Thu Oct 16 17:43:48 2008 +0100
+++ b/mde/gui/widget/Ifaces.d	Tue Oct 21 09:57:19 2008 +0100
@@ -135,11 +135,24 @@
  * A parent widget is responsible for setting the size of its children widgets, however it must
  * satisfy their minimal sizes as available from minWidth() and minHeight(). setWidth() and
  * setHeight() are called on all widgets after creation.
+ * 
+ * Also see finalize().
  *************************************************************************************************/
 //NOTE: add another this() without the data for default initialization, for the GUI editor?
 interface IChildWidget : IWidget
 {
 //BEGIN Load and save
+    // NOTE - change?
+    /** Called on all widgets after all widgets have been created in a deepest first order.
+     *
+     * Must be called before any other methods on the widget, which means this cannot call sub-
+     * widgets' methods, but finalize can. */
+    void prefinalize ();
+    void finalize ();   /// ditto
+    
+    /** Widget should return a list of all its children. */
+    IChildWidget[] children ();
+    
     /** 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.
@@ -177,10 +190,7 @@
     
     /** The minimal size the widget could be shrunk to (or its fixed size).
      *
-     * Takes into account child-widgets and any other contents.
-     * 
-     * Note: layout uses these calls to initialize it's alignment device. So, after creating a
-     * (layout) widget, minWidth should be the first function called on it! */
+     * Takes into account child-widgets and any other contents. */
     wdim minWidth ();
     wdim minHeight ();	/// ditto
     
--- a/mde/gui/widget/Widget.d	Thu Oct 16 17:43:48 2008 +0100
+++ b/mde/gui/widget/Widget.d	Tue Oct 21 09:57:19 2008 +0100
@@ -49,7 +49,17 @@
         this.mgr = mgr;
     }
     
+    // Most widgets don't need this; all initialization os usually done in this()
+    void prefinalize () {}
+    void finalize () {}
+    
+    // ParentWidget is inteded for parent widgets to derive
+    IChildWidget[] children () {
+        return null;
+    }
+    
     // Don't save any data: fine for many widgets.
+    // FIXME: implementation could be added to ParentWidget
     bool saveChanges (widgetID) {
         return false;
     }
@@ -141,6 +151,23 @@
     				// resizible; both types of widgets should actually be expandable.
 }
 
+/*************************************************************************************************
+* An abstract base widget class for parent widgets.
+*************************************************************************************************/
+abstract class ParentWidget : Widget
+{
+    this (IWidgetManager mgr, widgetID id, WidgetData data) {
+        super (mgr, id, data);
+    }
+    
+    IChildWidget[] children () {
+        return subWidgets;
+    }
+    
+protected:
+    IChildWidget[] subWidgets;
+}
+
 /** A base for fixed-size widgets taking their size from the creation data. */
 class FixedWidget : Widget {
     // Check data.length is at least 3 before calling!
--- a/mde/gui/widget/layout.d	Thu Oct 16 17:43:48 2008 +0100
+++ b/mde/gui/widget/layout.d	Tue Oct 21 09:57:19 2008 +0100
@@ -114,17 +114,26 @@
         OptionList optsList = OptionList.trial();
         rows = optsList.list.length;
         cols = 1;
+        sWId = data.strings[0];
         
         // Get all sub-widgets
         subWidgets.length = rows*cols;
         foreach (i, c; optsList.list) {
-            subWidgets[i] = mgr.makeWidget (data.strings[0], c);
+            subWidgets[i] = mgr.makeWidget (sWId, c);
         }
         super (mgr, id, data);
     }
     
+    bool saveChanges (widgetID id) {
+        // Since all sub-widgets have the same id, it only makes sense to call on one
+        if (subWidgets is null)
+            return false;
+        return subWidgets[0].saveChanges (sWId);
+    }
+    
 private:
     OptionList optsList;
+    widgetID sWId;      // sub-widget's ID, for calling saveChanges FIXME no longer pass?
 }
 
 
@@ -138,7 +147,7 @@
  *
  * The grid has no border but has spacing between widgets.
  *************************************************************************************************/
-abstract class GridWidget : Widget
+abstract class GridWidget : ParentWidget
 {
     //BEGIN Creation & saving
     /** Partial constructor for a grid layout widget.
@@ -148,7 +157,10 @@
      * the call to genCachedConstructionData can be moved to the derived this() methods.)
      * 
      * Derived constructors may also set initWidths to the array of column widths followed by
-     * row heights used to initially set the row/column dimensions. */
+     * row heights used to initially set the row/column dimensions.
+     * 
+     * Sub-widgets are finalized here, so no methods should be called on sub-widgets before calling
+     * this super. */
     protected this (IWidgetManager mgr, widgetID id, WidgetData data) {
         super (mgr, id, data);
         
@@ -163,40 +175,39 @@
         else
             row = (new AlignColumns (rows));
         row.addSetCallback (&setRowHeight);
-        
-        // Calculate cached construction data
-        genCachedConstructionData;
+    }
+    
+    /** Prior to finalizing but after sub-widgets are finalized, some information needs to be
+     * passed to the AlignColumns. */
+    void prefinalize () {
+        genCachedConstructionData;  // min widths, sizableness
     }
     
     /** Responsible for calculating the minimal size and initializing some stuff.
      *
      * As such, this must be the first function called after this(). */
-    wdim minWidth () {
-        if (!alignInit) {       // assumes col & row.width are initialized simultaneously
-            alignInit = true;
-            if (initWidths) {
-                debug assert (initWidths.length == cols + rows, "initWidths provided but has bad length");
-                col.setWidths (initWidths[0..cols]);
-                row.setWidths (initWidths[cols..$]);
-                initWidths = null;  // free
-            } else {
-                col.setWidths;
-                row.setWidths;
-            }
-            
-            mw = col.mw;
-            mh = row.mw;
-            w = col.w;
-            h = row.w;
-            
-            // Tell subwidgets their new sizes. Positions are given by a later call to setPosition.
-            foreach (i,widget; subWidgets) {
-                // Resizing direction is arbitrarily set to negative:
-                widget.setWidth  (col.width[i % cols], -1);
-                widget.setHeight (row.width[i / cols], -1);
-            }
+    void finalize () {
+        if (initWidths) {
+            debug assert (initWidths.length == cols + rows, "initWidths provided but has bad length");
+            col.setWidths (initWidths[0..cols]);
+            row.setWidths (initWidths[cols..$]);
+            initWidths = null;  // free
+        } else {
+            col.setWidths;
+            row.setWidths;
         }
-        return mw;
+        
+        mw = col.mw;
+        mh = row.mw;
+        w = col.w;
+        h = row.w;
+        
+        // Tell subwidgets their new sizes. Positions are given by a later call to setPosition.
+        foreach (i,widget; subWidgets) {
+            // Resizing direction is arbitrarily set to negative:
+            widget.setWidth  (col.width[i % cols], -1);
+            widget.setHeight (row.width[i / cols], -1);
+        }
     }
     //END Creation & saving
     
@@ -279,9 +290,10 @@
      * Also need to be re-run if the renderer changes.
      *
      * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns
-     * (col and row). */
+     * (col and row). subWidgets need to know their minimal size and resizability. */
     void genCachedConstructionData () {
         // Will only change if renderer changes:
+        // NOTE shared AlignColumns get this set by all sharing GridWidgets
         col.spacing = row.spacing = mgr.renderer.layoutSpacing;
         
         // Calculate the minimal column and row sizes:
@@ -361,11 +373,10 @@
     
     myIt cols, rows;	// number of cells in grid
     wdim[] initWidths;  // see this / setInitialSize
-    bool alignInit;     // have AlignColumns instances been initialized?
     
     /* All widgets in the grid, by row. Order:  [ 0 1 ]
      *                                          [ 2 3 ] */
-    IChildWidget[] subWidgets;
+    //IChildWidget[] subWidgets;
     
     AlignColumns col, row;
 }