diff mde/gui/widget/layout.d @ 91:4d5d53e4f881

Shared alignment for dynamic content lists - finally implemented! Lots of smaller changes too. Some debugging improvements. When multiple .mtt files are read for merging, files with invalid headers are ignored and no error is thrown so long as at least one file os valid.
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 16 Oct 2008 17:43:48 +0100
parents b525ff28774b
children 085f2ca31914
line wrap: on
line diff
--- a/mde/gui/widget/layout.d	Wed Oct 01 23:37:51 2008 +0100
+++ b/mde/gui/widget/layout.d	Thu Oct 16 17:43:48 2008 +0100
@@ -23,6 +23,8 @@
 import mde.gui.content.options;
 import mde.gui.content.Content;
 
+import tango.util.container.HashMap;
+
 debug {
     import tango.util.log.Log : Log, Logger;
     private Logger logger;
@@ -46,24 +48,29 @@
     /** Constructor for a grid layout widget.
      *
      * Widget uses the initialisation data:
-     * [widgetID, r, c, w11, w12, ..., w1c, ..., wr1, ..., wrc]
-     * where r and c are the number of rows and columns, and wij is the ID (from parent Window's
+     * ---
+     * ints = [widget_type, align_flags, rows, cols]
+     * // or with column widths and row heights:
+     * ints = [widget_type, align_flags, rows, cols, col1width, ..., colCwidth, row1height, ..., rowRheight]
+     * strings = [w11, w12, ..., w1C, ..., wR1, ..., wRC]
+     * ---
+     * where R and C are the number of rows and columns, and wij is the ID (from parent Window's
      * list) for the widget in row i and column j. The number of parameters must be r*c + 3.
      * 
      * The content parameter is passed on to all children accepting an IContent. */
-    this (IWidgetManager mgr, WidgetData data, IContent content) {
+    this (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) {
         // Get grid size and check data
-        // Check sufficient data for rows, cols, and possibly row/col widths.
-        if (data.ints.length < 3) throw new WidgetDataException;
+        // Check sufficient data for type, align-flags, rows, cols, and possibly row/col widths.
+        if (data.ints.length < 4) throw new WidgetDataException (this);
         
-        rows = data.ints[1];
-        cols = data.ints[2];
+        rows = data.ints[2];
+        cols = data.ints[3];
         // Check: at least one sub-widget, ints length == 3 or also contains row & col widths,
         // strings' length is correct:
         if (rows < 1 || cols < 1 ||
-            (data.ints.length != 3 && data.ints.length != 3 + rows + cols) ||
+            (data.ints.length != 4 && data.ints.length != 4 + rows + cols) ||
             data.strings.length != rows * cols)
-            throw new WidgetDataException;
+            throw new WidgetDataException (this);
         this.data = data;
         
         // Get all sub-widgets
@@ -72,16 +79,10 @@
             subWidget = mgr.makeWidget (data.strings[i], content);
         }
         
-        super (mgr, data);
+        if (data.ints.length == 4 + rows + cols)
+            initWidths = cast(wdim[]) data.ints[4..$];
         
-        if (data.ints.length == 3 + rows + cols) {
-            col.setWidths (cast(wdim[]) data.ints[3..cols+3]);
-            row.setWidths (cast(wdim[]) data.ints[cols+3..$]);
-        } else {
-            col.setWidths;
-            row.setWidths;
-        }
-        adjustCache;
+        super (mgr, id, data);
     }
     
     // Save column/row sizes. Currently always do so.
@@ -90,7 +91,7 @@
             foreach (i, widget; subWidgets) // recurse on subwidgets
                 widget.saveChanges (strings[i]);
             
-            ints = ints[0..3] ~ cast(int[])col.width ~ cast(int[])row.width;
+            ints = ints[0..4] ~ cast(int[])col.width ~ cast(int[])row.width;
         }
         mgr.setData (id, data);
         return true;
@@ -105,10 +106,10 @@
  *************************************************************************************************/
 class TrialContentLayoutWidget : GridWidget
 {
-    this (IWidgetManager mgr, WidgetData data) {
+    this (IWidgetManager mgr, widgetID id, WidgetData data) {
         debug scope (failure)
                 logger.warn ("TrialContentLayoutWidget: failure");
-        WDCheck (data, 1, 1);
+        WDCheck (data, 2, 1);
         
         OptionList optsList = OptionList.trial();
         rows = optsList.list.length;
@@ -119,12 +120,7 @@
         foreach (i, c; optsList.list) {
             subWidgets[i] = mgr.makeWidget (data.strings[0], c);
         }
-        super (mgr, data);
-        
-        // Set col/row widths to minimals.
-        col.setWidths;
-        row.setWidths;
-        adjustCache;
+        super (mgr, id, data);
     }
     
 private:
@@ -151,34 +147,56 @@
      * before calling this super constructor. (If it's necessary to call super(...) first,
      * the call to genCachedConstructionData can be moved to the derived this() methods.)
      * 
-     * Derived constructors should call setWidths on col and row, and then call
-     * adjustCache, after calling this. */
-    protected this (IWidgetManager mgr, WidgetData data) {
-        super (mgr, data);
+     * Derived constructors may also set initWidths to the array of column widths followed by
+     * row heights used to initially set the row/column dimensions. */
+    protected this (IWidgetManager mgr, widgetID id, WidgetData data) {
+        super (mgr, id, data);
         
         // Create cell aligners with appropriate col/row adjustment function
-        col = (new AlignColumns (cols)).addSetCallback (&setColWidth);
-        row = (new AlignColumns (rows)).addSetCallback (&setRowHeight);
+        if (data.ints[1] & 1)
+            col = AlignColumns.getInstance (id, cols);
+        else
+            col = (new AlignColumns (cols));
+        col.addSetCallback (&setColWidth);
+        if (data.ints[1] & 2)
+            row = AlignColumns.getInstance (id~"R", rows);      // id must be unique to that for cols!
+        else
+            row = (new AlignColumns (rows));
+        row.addSetCallback (&setRowHeight);
         
         // Calculate cached construction data
         genCachedConstructionData;
     }
     
-    /** Generates cached mutable data.
+    /** Responsible for calculating the minimal size and initializing some stuff.
      *
-     * Should be called by adjust() after calling setWidths. */
-    void adjustCache () {
-        // Generate cached mutable data
-        // Calculate column and row locations:
-        w = col.genPositions;
-        h = row.genPositions;
-        
-        // 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);
+     * 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);
+            }
         }
+        return mw;
     }
     //END Creation & saving
     
@@ -191,17 +209,11 @@
     }
     
     void setWidth (wdim nw, int dir) {
-        if (nw == w) return;
-        
-        w += col.adjustCellSizes (nw - w, (dir == -1 ? col.lastSizable : col.firstSizable), dir);
-        
+        w = col.resizeWidth (nw, dir);
         // Note: setPosition must be called after!
     }
     void setHeight (wdim nh, int dir) {
-        if (nh == h) return;
-        
-        h += row.adjustCellSizes (nh - h, (dir == -1 ? row.lastSizable : row.firstSizable), dir);
-        
+        h = row.resizeWidth (nh, dir);
         // Note: setPosition must be called after!
     }
     
@@ -209,6 +221,7 @@
         this.x = x;
         this.y = y;
         
+        debug assert (col.pos && row.pos, "setPosition: col/row.pos not set (code error)");
         foreach (i,widget; subWidgets)
             widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]);
     }
@@ -218,7 +231,9 @@
     // Find the relevant widget.
     IChildWidget getWidget (wdim cx, wdim cy) {
         debug scope (failure)
-                logger.warn ("getWidget: failure");
+            logger.warn ("getWidget: failure; values: click, pos, width - {}, {}, {} - {}, {}, {}", cx, x, w, cy, y, h);
+        debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)");
+        
         // Find row/column:
         myDiff i = col.getCell (cx - x);
         myDiff j = row.getCell (cy - y);
@@ -239,7 +254,7 @@
             * resizeRow is non-negative). */
             
             // find col/row's resizeD & resizeU
-            if (col.findResize (cx - x) && row.findResize (cy - y))
+            if (col.findResizeCols (cx - x) && row.findResizeCols (cy - y))
                 return;		// unable to resize
             
             dragX = cx;
@@ -263,15 +278,14 @@
      * (i.e. to produce cached data calculated from construction data).
      * Also need to be re-run if the renderer changes.
      *
-     * rows, cols and subWidgets must be set before calling. */
+     * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns
+     * (col and row). */
     void genCachedConstructionData () {
         // Will only change if renderer changes:
         col.spacing = row.spacing = mgr.renderer.layoutSpacing;
         
         // Calculate the minimal column and row sizes:
-        // set length, making sure the arrays are initialised to zero:
-        col.minWidth = new wdim[cols];
-        row.minWidth = new wdim[rows];
+        // AlignColumns (row, col) takes care of initializing minWidth.
         foreach (i,widget; subWidgets) {
             // Increase dimensions if current minimal size is larger:
             myIt n = i % cols;	// column
@@ -282,35 +296,16 @@
             if (row.minWidth[n] < md) row.minWidth[n] = md;
         }
         
-        
-        // Calculate the overall minimal size, starting with the spacing:
-        mh = mgr.renderer.layoutSpacing;	// use mh temporarily
-        mw = mh * cast(wdim)(cols - 1);
-        mh *= cast(wdim)(rows - 1);
-        
-        foreach (x; col.minWidth)		// add the column/row's dimensions
-            mw += x;
-        foreach (x; row.minWidth)
-            mh += x;
-        
-        
         // Find which cols/rows are resizable:
-        // reset:
-        col.sizable = new bool[cols];
-        row.sizable = new bool[rows];
-        col.firstSizable = row.firstSizable = -1;
-        
+        // AlignColumns initializes sizable, and sets first and last sizables.
         forCols:
         for (myIt i = 0; i < cols; ++i) {				// for each column
             for (myIt j = 0; j < subWidgets.length; j += cols)	// for each row
                 if (!subWidgets[i+j].isWSizable)	// column not resizable
                     continue forCols;			// continue the outer for loop
-                
+            
             // column is resizable if we get to here
             col.sizable[i] = true;
-            if (col.firstSizable < 0)
-                col.firstSizable = i;
-            col.lastSizable = i;
         }
         
         forRows:
@@ -319,10 +314,7 @@
                 if (!subWidgets[i+j].isHSizable)
                     continue forRows;
             
-            row.lastSizable = i / cols;
-            row.sizable[row.lastSizable] = true;
-            if (row.firstSizable < 0)
-                row.firstSizable = row.lastSizable;
+            row.sizable[i / cols] = true;
         }
     }
     //END Cache calculation functions
@@ -342,8 +334,8 @@
     
     //BEGIN Col/row resizing callback
     void resizeCallback (wdim cx, wdim cy) {
-        col.resize (cx - dragX);
-        row.resize (cy - dragY);
+        col.resizeCols (cx - dragX);
+        row.resizeCols (cy - dragY);
         
         // NOTE: all adjustments are relative; might be better if they were absolute?
         dragX = cx;
@@ -367,8 +359,9 @@
     wdim dragX, dragY;	// coords where drag starts
     //END Col/row resizing callback
     
-    
     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 ] */
@@ -377,13 +370,81 @@
     AlignColumns col, row;
 }
 
-/// Position information on top of widths.
-//FIXME - merge classes back together?
-class AlignColumns : AlignWidths
+
+/**************************************************************************************************
+ * Alignment device
+ * 
+ * E.g. can control widths of columns within a grid, and provide sensible resizing, respecting the
+ * minimal width required by each cell in a column. Is not restricted to horizontal widths, but to
+ * ease descriptions, a horizontal context (column widths) is assumed.
+ * 
+ * Cells should be of type IChildWidget.
+ * 
+ * Cells are not directly interacted with, but minimal widths for each column are passed, and
+ * callback functions are used to adjust the width of any column.
+ *************************************************************************************************/
+class AlignColumns
 {
-    /// See AlignWidths.this
+    /** Instance returned will be shared with any other widgets of same widgetID.
+     *
+     * Also ensures each widget sharing an instance expects the same number of columns. */
+    static AlignColumns getInstance (widgetID id, myIt columns) {
+        AlignColumns* p = id in instances;
+        if (p) {
+            if (p.minWidth.length != columns)
+                throw new GuiException ("AlignColumns: no. of columns varies between sharing widgets (code error)");
+            return *p;
+        } else {
+            auto a = new AlignColumns (columns);
+            instances[id] = a;
+            return a;
+        }
+    }
+    
+    /** Create an instance. After creation, the number of columns can only be changed by calling
+     * reset.
+     * 
+     * After creation, minimal widths should be set for all columns (minWidth) and
+     * setWidths must be called before other functions are used. */
     this (myIt columns) {
-        super (columns);
+        reset (columns);
+    }
+    
+    /** Reset all column information (only keep set callbacks).
+     *
+     * Widths should be set after calling, as on creation. */
+    void reset (myIt columns) {
+        if (columns < 1)
+            throw new GuiException("AlignColumns: created with <1 column");
+        minWidth = new wdim[columns];
+        sizable = new bool[columns];
+        width = null;   // enforce calling setWidths after this
+        firstSizable = -1;
+        lastSizable = -1;
+        spare = 0;
+    }
+    
+    /** Initialize widths as minimal widths. */
+    void setWidths () {
+        if (!width) {
+            width = minWidth.dup;
+            initCalc;
+        }
+    }
+    /** Initialize widths from supplied list, checking validity. */
+    void setWidths (wdim[] data) {
+        if (!width) {
+            // Set to provided data:
+            width = data;
+            // And check sizes are valid:
+            foreach (i, m; minWidth) {
+                // if fixed width or width is less than minimum:
+                if (!sizable[i] || width[i] < m) {
+                    width[i] = m;
+                }
+            }
+            initCalc;
+        }
     }
     
     /** Add a callback to be called to notify changes in a column's width.
@@ -396,39 +457,62 @@
         return this;
     }
     
-    /** Generate position infomation and return total width of columns. */
-    wdim genPositions () {
-        pos.length = minWidth.length;
-        
-        wdim x = 0;
-        foreach (i, w; width) {
-            pos[i] = x;
-            x += w + spacing;
-        }
-        return x - spacing;
-    }
-    
-    // Get the row/column a click occured in
-    // Returns -i if in space to left of col i, or i if on col i
+    /** Get the row/column of relative position l.
+     *
+     * returns:
+     * -i if in space to left of col i, or i if on col i, or -(num cols + 1) if in $(I spare)
+     * space to right of last column. */
     myDiff getCell (wdim l) {
         myDiff i = minWidth.length - 1;     // starting from right...
         while (l < pos[i]) {                // decrement while left of this column
             debug assert (i > 0, "getCell: l < pos[0] (code error)");
             --i;
         }                                   // now (l >= pos[i])
-        if (l >= pos[i] + width[i]) {       // between columns
-            debug assert (i+1 < minWidth.length, "getCell: l >= pos[$-1] + width[$-1] (code error)");
+        if (l >= pos[i] + width[i]) {       // between columns or in spare space after last column
+            debug assert (i+1 < minWidth.length ||
+                          l < pos[i] + width[i] + spare,
+                          "getCell: l >= total width (code error)");
             return -i - 1;                  // note: i might be 0 so cannot just return -i
         }
         return i;
     }
     
-    // Calculate resizeU/resizeD, and return true if unable to resize.
-    bool findResize (wdim l) {
+    /** Adjust total size with direction dir.
+     *
+     * nw should be at least the minimal width. */
+    wdim resizeWidth (wdim nw, int dir) {
+        if (nw < mw) {
+            debug logger.warn ("Widget dimension set below minimal");
+            nw = mw;
+        }
+        if (nw == w) return w;
+        
+        wdim diff = nw - w;
+        if (firstSizable == -1) {
+            spare += diff;
+            w = nw;
+        } else
+            adjustCellSizes (diff, (dir == -1 ? lastSizable : firstSizable), dir);
+        
+        debug if (nw != w) {
+            logger.trace ("resizeWidth to {} failed, new width: {}",nw,w);
+            /+ Also print column widths & positions:
+            logger.trace ("resizeWidth to {} failed! Column dimensions and positions:",nw);
+            foreach (i,w; width)
+                logger.trace ("\t{}\t{}", w,pos[i]);+/
+        }
+        return w;
+    }
+    
+    /** Calculate resizeU/resizeD, and return true if unable to resize.
+     *
+     * This and resizeCols are for moving dividers between cells. */
+    bool findResizeCols (wdim l) {
         resizeU = -getCell (l);             // potential start for upward-resizes
-        if (resizeU <= 0) return true;      // not on a space between cells
-            resizeD = resizeU - 1;              // potential start for downward-resizes
-            
+        if (resizeU <= 0 || resizeU > minWidth.length)
+            return true;        // not on a space between cells or in spare space after last cell
+        resizeD = resizeU - 1;              // potential start for downward-resizes
+        
         while (!sizable[resizeU]) {         // find first actually resizable column (upwards)
             ++resizeU;
             if (resizeU >= minWidth.length) {       // cannot resize
@@ -447,6 +531,59 @@
         
         return false;                       // can resize
     }
+    /// Resize columns based on findResizeCols
+    void resizeCols (wdim diff)
+    {
+        if (resizeU <= 0) return;
+        
+        // do shrinking first (in case we hit the minimum)
+        if (diff >= 0) {
+            diff = -adjustCellSizes (-diff, resizeU, 1);
+            adjustCellSizes (diff, resizeD, -1);
+        } else {
+            diff = -adjustCellSizes (diff, resizeD, -1);
+            adjustCellSizes (diff, resizeU, 1);
+        }
+    }
+    
+    /** Intitialization triggered by setWidths.
+     * 
+     * Calculates first/lastSizable from sizable, minimal width and positions. */
+    private void initCalc () {
+        /* Calculate the minimal width of all columns plus spacing. */
+        mw = spacing * cast(wdim)(minWidth.length - 1);
+        foreach (w; minWidth)
+            mw += w;
+        
+        genPositions;
+        foreach (i,s; sizable) {
+            if (s) {
+                firstSizable = i;
+                goto gotFirst;
+            }
+        }
+        return; // none resizable - don't search for lastSizable
+        gotFirst:
+        
+        foreach_reverse (i,s; sizable) {
+            if (s) {
+                lastSizable = i;
+                return; // done
+            }
+        }
+    }
+    
+    /* Generate position infomation for each column and set w. */
+    private void genPositions () {
+        pos.length = minWidth.length;
+        
+        w = 0;
+        foreach (i, cw; width) {
+            pos[i] = w;
+            w += cw + spacing;
+        }
+        w -= spacing;
+    }
     
     /* Adjust the total size of rows/columns (including spacing) by diff.
     *
@@ -461,15 +598,14 @@
     * Note: Check variable used for start is valid before calling! If a non-sizable column's
     *  index is passed, this should get increased (if diff > 0) but not decreased.
     */
-    wdim adjustCellSizes (wdim diff, myDiff start, int incr)
+    private wdim adjustCellSizes (wdim diff, myDiff start, int incr)
     in {
-        assert (width, "CellAlign.adjustCellSizes: width is null (code error)");
+        assert (width.length == minWidth.length, "CellAlign.adjustCellSizes: width is null (code error)");
         // Most likely if passed negative when sizing is disabled:
         assert (start >= 0 && start < minWidth.length, "adjustCellSizes: invalid start");
         debug assert (incr == 1 || incr == -1, "adjustCellSizes: invalid incr");
     } body {
-        debug scope(failure)
-        logger.trace ("adjustCellSizes: failure");
+        debug scope(failure) logger.trace ("adjustCellSizes: failure");
         myDiff i = start;
         if (diff > 0) {             // increase size of first resizable cell
             width[i] += diff;
@@ -512,103 +648,38 @@
         return diff;
     }
     
-    void resize (wdim diff)
-    {
-        if (resizeU <= 0) return;
-        
-        // do shrinking first (in case we hit the minimum)
-        if (diff >= 0) {
-            diff = -adjustCellSizes (-diff, resizeU, 1);
-            adjustCellSizes (diff, resizeD, -1);
-        } else {
-            diff = -adjustCellSizes (diff, resizeD, -1);
-            adjustCellSizes (diff, resizeU, 1);
-        }
-    }
-    
-private:
-    wdim[]  pos;                // relative position (cumulative width[i-1] plus spacing)
-    wdim    spacing;            // used by genPositions (which cannot access the layout class's data)
-    // Callbacks used to actually adjust a column's width:
-    void delegate (myIt,wdim,int) setWidthCb[]; // set width of a column, with resize direction
-}
-
-/**************************************************************************************************
- * Alignment device
- * 
- * E.g. can control widths of columns within a grid, and provide sensible resizing, respecting the
- * minimal width required by each cell in a column. Is not restricted to horizontal widths, but to
- * ease descriptions, a horizontal context (column widths) is assumed.
- * 
- * Cells should be of type IChildWidget.
- * 
- * FIXME:
- * Cells are not directly interacted with, but minimal widths for each column are passed, and
- * callback functions are used to adjust the width of any column.
- *************************************************************************************************/
-//FIXME: how to set cell positions after resizes?
-/+ FIXME - remove position information from here, removing need of horizontal alignment of cells
- into columns. On resizing, don't permanently adjust sizes until callback ends (mouse button
- released). Until then, always readjust from current "permanent" size. +/
-class AlignWidths
-{
-    /** Create an instance. After creation, the number of columns can only be changed by calling
-     * reset.
-     * 
-     * After creation, minimal widths should be set for all columns (minWidth) and
-     * setWidths must be called before other functions are used. */
-    this (myIt columns) {
-        reset (columns);
-    }
-    
-    /** Reset all column information (only keep set callbacks).
-     *
-     * Widths should be set after calling, as on creation. */
-    void reset (myIt columns) {
-        if (columns < 1)
-            throw new GuiException("AlignWidths: created with <1 column");
-        minWidth = new wdim[columns];
-        width = null;   // enforce calling setWidths after this
-    }
-    
-    /** Initialize widths as minimal widths. */
-    void setWidths () {
-        width = minWidth.dup;
-    }
-    /** Initialize widths from supplied list, checking validity. */
-    void setWidths (wdim[] data) {
-        // Set to provided data:
-        width = data;
-        // And check sizes are valid:
-        foreach (i, m; minWidth)
-            // if fixed width or width is less than minimum:
-            if (!sizable[i] || width[i] < m)
-                width[i] = m;
-    }
-    
     /** Minimal width for each column.
      *
-     * Initialized to zero. Each class using this AlignWidths should, for each column, increase
+     * Initialized to zero. Each class using this AlignColumns should, for each column, increase
      * this value to the maximum of the minimal widths (in other words, set
      * minWidth[i] = max(minWidth[i], cell.minWidth) for each cell in column i). */
     wdim[]  minWidth;           // minimal widths (set by genCachedConstructionData)
     
     /** For each column i, sizable[i] is true if that column is resizable.
-     * firstSizable and lastSizable are the indicies of the first/last resizable column (negative
-     * if none are resizable).
      * 
      * Set along with minWidth before calling setWidths. */
     bool[]  sizable;            // set by genCachedConstructionData
-    /// ditto
-    myDiff  firstSizable, lastSizable;  // set by genCachedConstructionData
     
-    /** Current width for each column.
+    /** Current width, relative position (for each column)
      *
      * Treat as READ ONLY! */
     wdim[]  width;              // only adjusted within the class
-protected:
-    myDiff  resizeD,            // resize down from this index (<0 if not resizing)
+    wdim[]  pos;                /// ditto
+    protected:
+    myDiff  resizeD,            // resizeCols works down from this index (<0 if not resizing)
             resizeU;            // and up from this index
+    wdim    spacing;            // used by genPositions (which cannot access the layout class's data)
+    wdim    spare;              // fixed size only: extra blank space filler
+    wdim    w,mw;               // current & minimal widths
+    /* indicies of the first/last resizable column (negative if none are resizable). */
+    myDiff  firstSizable = -1, lastSizable = -1;  // set by calcFLSbl
+    // Callbacks used to actually adjust a column's width:
+    void delegate (myIt,wdim,int) setWidthCb[]; // set width of a column, with resize direction
+    
+    static HashMap!(widgetID,AlignColumns) instances;
+    static this () {
+        instances = new HashMap!(widgetID,AlignColumns);
+    }
 }
 
 // Index types. Note that in some cases they need to hold negative values.