diff mde/gui/widget/layout.d @ 111:1655693702fc

Resolved ticket #4, allowing widgets to reload strings and recalculate sizes mid-run. Removed prefinalize and finalize and added setup as the new second initialization phase, which can be re-run.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 06 Dec 2008 17:41:42 +0000
parents 2a1428ec5344
children fe061009029d
line wrap: on
line diff
--- a/mde/gui/widget/layout.d	Fri Dec 05 11:29:39 2008 +0000
+++ b/mde/gui/widget/layout.d	Sat Dec 06 17:41:42 2008 +0000
@@ -152,57 +152,58 @@
      * 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.
-     * 
-     * Sub-widgets are finalized here, so no methods should be called on sub-widgets before calling
-     * this super. */
+     * 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
         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!
+	    col = AlignColumns.getInstance (id, cols);
         else
-            row = (new AlignColumns (rows));
-        row.addSetCallback (&setRowHeight);
+	    col = (new AlignColumns (cols));
+        col.addCallbacks (&setColWidth, &setupAlignDimData);
+        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.addCallbacks (&setRowHeight, &setupAlignDimData);
         useSpacing = (data.ints[1] & 4) != 0;
     }
     
-    /** 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(). */
-    void finalize () {
-        if (initWidths.length == cols + rows) {
-            col.setWidths (initWidths[0..cols]);
-            row.setWidths (initWidths[cols..$]);
-        } else {
-            col.setWidths;
-            row.setWidths;
-        }
-        initWidths = null;  // free
-        
-        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);
-        }
+    bool setup (uint n, uint flags) {
+	// Run all internal calculations regardless of changes, then check dimensions for changes.
+	// Don't try shortcutting internal calculations when there are no changes - I've tried, and
+	// doing so adds enough overhead to make doing so almost(?) worthless (or at least large
+	// increases in complexity).
+	wdim ow = w, oh = h;
+	
+	col.setup (n, flags);
+	row.setup (n, flags);
+	
+	if (initWidths.length == cols + rows) {
+	    col.setWidths (initWidths[0..cols]);
+	    row.setWidths (initWidths[cols..$]);
+	} else {
+	    col.setWidths;
+	    row.setWidths;
+	}
+	initWidths = null;  // free
+	
+	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 (ow != w || oh != h);
     }
     //END Creation & saving
     
@@ -284,26 +285,34 @@
     
 package:
     /* Calculations which need to be run whenever a new sub-widget structure is set
-     * (i.e. to produce cached data calculated from construction data).
-     * Also need to be re-run if the renderer changes.
+     * or other changes affecting widget sizes. Most of these need to happen regardless of whether
+     * changes have occurred, since AlignColumns have been reset.
      *
      * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns
      * (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
+    void setupAlignDimData (uint n, uint flags) {
+	if (sADD_n == n) return;	// cached data is current
+	sADD_n = n;
+	
+	foreach (widg; subWidgets)	// make sure all subwidgets have been set up
+	    widg.setup (n,flags);
+	// make sure both AlignColumns are set up (since first call to setup(n) calls reset):
+	col.setup (n, flags);
+	row.setup (n, flags);
+	
+	// Note: shared AlignColumns get this set by all sharing GridWidgets
         col.spacing = row.spacing = useSpacing ? mgr.renderer.layoutSpacing : 0;
         
         // Calculate the minimal column and row sizes:
         // 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
+            myIt j = i % cols;	// column
             wdim md = widget.minWidth;
-            if (col.minWidth[n] < md) col.minWidth[n] = md;
-            n = i / cols;		// row
+            if (col.minWidth[j] < md) col.minWidth[j] = md;
+            j = i / cols;		// row
             md = widget.minHeight;
-            if (row.minWidth[n] < md) row.minWidth[n] = md;
+            if (row.minWidth[j] < md) row.minWidth[j] = md;
         }
         
         // Find which cols/rows are resizable:
@@ -370,7 +379,8 @@
     
     myIt cols, rows;	// number of cells in grid
     wdim[] initWidths;  // see this / setInitialSize
-    bool useSpacing;	// true if spacing should be applied
+    uint sADD_n = uint.max;	// param n of last setup call after setupAlignDimData has run
+    bool useSpacing;	// add inter-row/col spacing?
     
     /* All widgets in the grid, by row. Order:  [ 0 1 ]
      *                                          [ 2 3 ] */
@@ -417,18 +427,31 @@
      * 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);
+	if (columns < 1)
+	    throw new GuiException("AlignColumns: created with <1 column (code error)");
+	minWidth.length = columns;
+	sizable.length = columns;
+    }
+    
+    /** Like IChildWidget's setup; calls sADD delegates. */
+    void setup (uint n, uint flags) {
+	if (n != setup_n) {
+	    logger.trace ("AlignColumns.setup ({}): {}", n, cast(void*)this);
+	    setup_n = n;
+	    setupWidths = false;
+	    reset (minWidth.length);
+	    
+	    foreach (dg; sADD)
+		dg (n, flags);	// set flag 1
+	}
     }
     
     /** 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 (code error)");
-        minWidth = new wdim[columns];
-        sizable = new bool[columns];
-        width = null;   // enforce calling setWidths after this
+        minWidth[] = 0;
+        sizable[] = false;
         firstSizable = -1;
         lastSizable = -1;
     }
@@ -438,11 +461,14 @@
      * Also calculates first/lastSizable from sizable, overall minimal width and column positions.
      */
     void setWidths (wdim[] data = null) {
-        if (!width) {
-            if (data) {
-                debug assert (data.length == minWidth.length, "setWidths called with bad data length (code error)");
-                width = data.dup;       // data is shared by other widgets with same id so must be .dup'ed
-                // And check sizes are valid:
+	if (!setupWidths) {
+	    logger.trace ("setWidths");
+	    setupWidths = true;
+            if (data || width) {	// use existing/external data: need to check validity
+                if (data) {
+                    assert (data.length == minWidth.length, "setWidths called with bad data length (code error)");
+                    width = data.dup;	// data is shared by other widgets with same id so must be .dup'ed
+                }
                 foreach (i, m; minWidth) {
                     if (!sizable[i] || width[i] < m)    // if width is fixed or less than minimum
                         width[i] = m;
@@ -474,13 +500,12 @@
         }
     }
     
-    /** Add a callback to be called to notify changes in a column's width.
-    * 
-    * All callbacks added are called on a width change so that multiple objects may share a
-    * CellAlign object. */
-    typeof(this) addSetCallback (void delegate (myIt,wdim,int) setCW) {
-        assert (setCW, "CellAlign.this: setCW is null (code error)");
+    /** Add a callback to be called to notify changes in a column's width, and the sADD callback.
+     */
+    typeof(this) addCallbacks (void delegate (myIt,wdim,int) setCW, void delegate (uint,uint) sDg) {
+	assert (setCW && sDg, "AlignColumns.addCallbacks: null callback (code error)");
         setWidthCb ~= setCW;
+	sADD ~= sDg;
         return this;
     }
     
@@ -669,6 +694,10 @@
     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
+    void delegate (uint,uint) sADD[];	// setupAlignDimData dlgs
+    
+    uint setup_n = uint.max;	// param n of last setup call
+    bool setupWidths;		// setWidths has been run
     
     static HashMap!(widgetID,AlignColumns) instances;
     static this () {