changeset 46:03fa79a48c48

Fixed resizing bugs in previous commit and made code cleaner and more efficient. setSize replaced by setWidth & setHeight. setPosition must be called after setWidth/Height. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 22 May 2008 12:51:47 +0100
parents 0fd51d2c6c8a
children e0839643ff52
files codeDoc/jobs.txt data/conf/gui.mtt mde/gui/widget/Ifaces.d mde/gui/widget/Widget.d mde/gui/widget/Window.d mde/gui/widget/layout.d
diffstat 6 files changed, 118 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/codeDoc/jobs.txt	Thu May 22 11:34:09 2008 +0100
+++ b/codeDoc/jobs.txt	Thu May 22 12:51:47 2008 +0100
@@ -56,6 +56,3 @@
 
 
 Done (for git log message):
-Moved the implementable widgets from mde.gui.widget.Widget to miscWidgets, leaving base widgets in Widget.
-Rewrote some of GridLayoutWidget's implementation. Made many operations general to work for either columns or rows. Some optimisations were intended but ended up being removed due to problems.
-Allowed layout's to resize from either direction (only with window resizes).
--- a/data/conf/gui.mtt	Thu May 22 11:34:09 2008 +0100
+++ b/data/conf/gui.mtt	Thu May 22 12:51:47 2008 +0100
@@ -11,4 +11,4 @@
 {WEmbedded}
 <int|x=20>
 <int|y=100>
-<int[][int]|widgetData=[1:[0x4010,50,50],2:[0xB004,3,1,3,1,3],3:[0x3001],0:[0xB004,3,1,3,1,3]]>
+<int[][int]|widgetData=[1:[0x4010,50,50],2:[0xB004,3,1,3,1,3],3:[0x3001],0:[0xB004,3,1,2,1,2]]>
--- a/mde/gui/widget/Ifaces.d	Thu May 22 11:34:09 2008 +0100
+++ b/mde/gui/widget/Ifaces.d	Thu May 22 12:51:47 2008 +0100
@@ -21,7 +21,12 @@
 
 /** Interface for Window, allowing widgets to call some of Window's methods.
  *
- * Contains the methods in Window available for widgets to call on their root. */
+ * Contains the methods in Window available for widgets to call on their root.
+ *
+ * Notation:
+ *  Positive/negative direction: along the x/y axis in this direction.
+ *  Layout widget: a widget containing multiple sub-widges (which hence controls how they are laid
+ *  out). */
 interface IWindow : IWidget
 {
     /** Widget ID type. Each ID is unique under this window.
@@ -119,9 +124,12 @@
     
     /** Used to adjust the size.
      *
-     * w,h is the new size. The boolean parameters describe which direction to resize from and is
-     * only really relevent to layout widgets (see GridLayoutWidget's implementation). When
-     * calling, just past true,true if it doesn't matter.
+     * Params:
+     *  nw/nh	= The new width/height
+     *  dir	= Direction to resize from. This is only really applicable to layout widgets.
+     *  	  It must be either -1 (start resizing from highest row/col index, decreasing the
+     *  	  index as necessary), or +1 (resize from the lowest index, i.e. 0).
+     *  	  Most widgets can simply ignore it.
      *
      * Implementation:
      * The size should be clamped to the widget's minimal size, i.e. the size set may be larger
@@ -130,8 +138,10 @@
      * This should be true for both resizable and fixed widgets; fixed widgets may still be scaled
      * to fill a whole row/column in a layout widget.
      *
-     * If the actual size is needed, call getCurrentSize afterwards. */
-    void setSize (int w, int h, bool, bool);
+     * If the actual size is needed, call getCurrentSize afterwards. setPosition must be called
+     * afterwards if the widget might be a layout widget. */
+    void setWidth (int nw, int dir);
+    void setHeight (int nh, int dir);	/// ditto
     
     /** Set the current position (i.e. called on init and move). */
     void setPosition (int x, int y);
--- a/mde/gui/widget/Widget.d	Thu May 22 11:34:09 2008 +0100
+++ b/mde/gui/widget/Widget.d	Thu May 22 12:51:47 2008 +0100
@@ -40,7 +40,8 @@
     // Most widgets don't need to do adjustments based on mutable data, however they usually do
     // still need to set their size.
     int[] adjust (int[] data) {
-        setSize (0,0,true,true);
+        setWidth (0,-1);
+        setHeight (0,-1);
         return data;
     }
     
@@ -72,8 +73,10 @@
     
     /* Set size: minimal size is (mw,mh). Note that both resizable and fixed widgets should allow
      * enlarging, so in both cases this is a correct implementation. */
-    void setSize (int nw, int nh, bool, bool) {
+    void setWidth (int nw, int) {
         w = (nw >= mw ? nw : mw);
+    }
+    void setHeight (int nh, int) {
         h = (nh >= mh ? nh : mh);
     }
     
--- a/mde/gui/widget/Window.d	Thu May 22 11:34:09 2008 +0100
+++ b/mde/gui/widget/Window.d	Thu May 22 12:51:47 2008 +0100
@@ -86,6 +86,11 @@
         widgetY = y + border.t;         // must be updated if the window is moved
         widget.setPosition (widgetX, widgetY);
         
+        // Calculate mw/mh and xw/yh (cached data):
+        widget.getMinimalSize (mw, mh);
+        mw += border.l + border.r;
+        mh += border.t + border.b;
+        
         xw = x+w;
         yh = y+h;
     }
@@ -206,11 +211,7 @@
     }
     
     void getMinimalSize (out int wM, out int hM) {
-        if (mh < 0) {       // calculate if necessary
-            widget.getMinimalSize (mw, mh);
-            mw += border.l + border.r;
-            mh += border.t + border.b;
-        }
+        // mw/mh are calculated by finalise();
         wM = mw;
         hM = mh;
     }
@@ -219,17 +220,17 @@
         ch = h;
     }
     
-    void setSize (int nw, int nh, bool wB, bool hB) {
-        getMinimalSize (w,h);
-        if (nw > w) w = nw;     // expand if new size is larger, but don't go smaller
-        if (nh > h) h = nh;
-        
-        xw = x+w;
-        yh = y+h;
-        
-        widget.setSize (w - border.l - border.r, h - border.t - border.b, wB, hB);
-        
-        gui_.requestRedraw ();  // obviously necessary whenever the window's size is changed
+    void setWidth (int nw, int dir) {
+        if (nw < mw) w = mw;	// clamp
+        else w = nw;
+        xw = x + w;
+        widget.setWidth (w - border.l - border.r, dir);
+    }
+    void setHeight (int nh, int dir) {
+        if (nh < mh) h = mh;	// clamp
+        else h = nh;
+        yh = y + h;
+        widget.setHeight (h - border.t - border.b, dir);
     }
     
     void setPosition (int nx, int ny) {
@@ -244,7 +245,7 @@
         
         widget.setPosition (widgetX, widgetY);
         
-        gui_.requestRedraw ();  // obviously necessary whenever the window is moved
+        gui_.requestRedraw ();  // necessary whenever the window is moved; setPosition is called after resizes and moves
     }
     
     IWidget getWidget (int cx, int cy) {
@@ -307,35 +308,31 @@
                 logger.trace ("resizeCallback: failure");
         
         // This function is only called if some resize is going to happen.
-        // To improve efficiency, store parameters to resize to.
-        int xSize = w, ySize = h;	// new size
-        int xDiff, yDiff;		// difference to new position
-        bool xHigh, yHigh;		// resize from positive side
+        // x,y are used as parameters to setPosition as well as being affected by it; somewhat
+        // pointless but fairly effective.
         
         if (resizeType & RESIZE_TYPE.L) {
-            getMinimalSize (xDiff, xSize);	// (only want xDiff, temporarily used as mw)
-            xSize = xDrag - cx;
-            if (xSize < xDiff) xSize = xDiff;	// clamp
-            xDiff = w - xSize;			// now used as amount to move
+            int xSize = xDrag - cx;
+            if (xSize < mw) xSize = mw;	// clamp
+            x += w - xSize;
+            setWidth (xSize, 1);
         }
         else if (resizeType & RESIZE_TYPE.R) {
-            xSize = xDrag + cx;
-            xHigh = true;
+            setWidth (xDrag + cx, -1);
         }
         if (resizeType & RESIZE_TYPE.T) {
-            getMinimalSize (ySize, yDiff);
-            ySize = yDrag - cy;
-            if (ySize < yDiff) ySize = yDiff;
-            yDiff = h - ySize;
+            int ySize = yDrag - cy;
+            if (ySize < mh) ySize = mh;
+            y += h - ySize;
+            setHeight (ySize, 1);
         }
         else if (resizeType & RESIZE_TYPE.B) {
-            ySize = yDrag + cy;
-            yHigh = true;
+            setHeight (yDrag + cy, -1);
         }
         
-        setSize (xSize, ySize, xHigh, yHigh);
-        if (xDiff != 0 || yDiff != 0)
-            setPosition (x + xDiff, y + yDiff);
+        // Moves lower (x,y) coordinate if necessary and repositions any sub-widgets moved by the
+        // resizing:
+        setPosition (x, y);
     }
     bool endCallback (ushort cx, ushort cy, ubyte b, bool state) {
         if (b == 1 && state == false) {
--- a/mde/gui/widget/layout.d	Thu May 22 11:34:09 2008 +0100
+++ b/mde/gui/widget/layout.d	Thu May 22 12:51:47 2008 +0100
@@ -37,6 +37,7 @@
 * The grid has no border but has spacing between widgets. */
 class GridLayoutWidget : Widget
 {
+    //BEGIN Creation & saving
     this (IWindow wind, int[] data) {
         // Get grid size and check data
         // Check sufficient data for rows, cols, and at least one widget:
@@ -62,6 +63,10 @@
         genCachedConstructionData;
     }
     
+    /* This does two things:
+     * 1. Pass adjust data on to sub-widgets
+     * 2. Set the size, from the adjust data if possible
+     */
     int[] adjust (int[] data) {
         // Give all sub-widgets their data:
         foreach (widget; subWidgets)
@@ -89,9 +94,11 @@
         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 "high direction":
-            widget.setSize (col.width[i % cols], row.width[i / cols], true, true);
+        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 data[lenUsed..$];
     }
@@ -115,7 +122,9 @@
         ret ~= col.width ~ row.width;
         return ret;
     }
+    //END Creation & saving
     
+    //BEGIN Size & position
     bool isWSizable () {
         return col.firstSizable >= 0;
     }
@@ -129,28 +138,19 @@
         mh = this.mh;
     }
     
-    void setSize (int nw, int nh, bool wHigh, bool hHigh) {
-        debug scope (failure) {
-            char[128] tmp;
-                logger.trace ("setSize failed: hHigh = " ~ (hHigh ? "true" : "false"));
-                logger.trace (logger.format (tmp, "rows to resize: {}, {}", row.firstSizable, row.lastSizable));
-        }
-        // Optimisation (could easily be called with same sizes if a parent layout widget is
-        // resized, since many columns/rows may not be resized).
-        if (nw == w && nh == h) return;
+    void setWidth (int nw, int dir) {
+        if (nw == w) return;
+        
+        w += col.adjustCellSizes (nw - w, (dir == -1 ? col.lastSizable : col.firstSizable), dir);
         
-        // calculate the row/column sizes (and new positions)
-        if (wHigh)
-            w += col.adjustCellSizes (nw - w, col.lastSizable, -1);
-        else
-            w += col.adjustCellSizes (nw - w, col.firstSizable, 1);
-        if (hHigh)
-            h += row.adjustCellSizes (nh - h, row.lastSizable, -1);
-        else
-            h += row.adjustCellSizes (nh - h, row.firstSizable, 1);
+        // Note: setPosition must be called after!
+    }
+    void setHeight (int nh, int dir) {
+        if (nh == h) return;
         
-        // set the sub-widget's sizes & positions
-        setSubWidgetSP (wHigh, hHigh);
+        h += row.adjustCellSizes (nh - h, (dir == -1 ? row.lastSizable : row.firstSizable), dir);
+        
+        // Note: setPosition must be called after!
     }
     
     void setPosition (int x, int y) {
@@ -160,6 +160,7 @@
         foreach (i,widget; subWidgets)
             widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]);
     }
+    //END Size & position
     
     
     // Find the relevant widget.
@@ -210,6 +211,8 @@
      * (i.e. to produce cached data calculated from construction data). */
     void genCachedConstructionData () {
         col.spacing = row.spacing = window.renderer.layoutSpacing;
+        col.setColWidth = &setColWidth;
+        row.setColWidth = &setRowHeight;
         
         // Calculate the minimal column and row sizes:
         // set length, making sure the arrays are initialised to zero:
@@ -269,21 +272,22 @@
                 row.firstSizable = row.lastSizable;
         }
     }
-    
-    // set sub-widgets size & position (done after resizing widget or rows/columns)
-    void setSubWidgetSP (bool wH, bool hH) {
-        for (myIt i = 0; i < cols; ++i)
-            for (myIt j = 0; j < rows; ++j)
-        {
-            IWidget widget = subWidgets[i + cols*j];
-            widget.setSize (col.width[i], row.width[j], wH, hH);
-            widget.setPosition (x + col.pos[i], y + row.pos[j]);
-        }
-    }
     //END Cache calculation functions
     
     
-    //BEGIN Col/row resizing
+    void setColWidth (myIt i, int w, int dir) {
+        for (myIt j = 0; j < rows; ++j) {
+            subWidgets[i + cols*j].setWidth (w, dir);
+        }
+    }
+    void setRowHeight (myIt j, int h, int dir) {
+        for (myIt i = 0; i < cols; ++i) {
+            subWidgets[i + cols*j].setHeight (h, dir);
+        }
+    }
+    
+    
+    //BEGIN Col/row resizing callback
     void resizeCallback (ushort cx, ushort cy) {
         col.resize (cx - dragX);
         row.resize (cy - dragY);
@@ -292,10 +296,9 @@
         dragX = cx;
         dragY = cy;
         
-        // NOTE: Resizing direction is set to "high direction" which isn't always going to be
-        // correct. A more accurate but more complex approach might be to get
-        // adjustCellSizes to do the work.
-        setSubWidgetSP (true, true);
+        foreach (i,widget; subWidgets)
+            widget.setPosition (x + col.pos[i % cols],
+                                y + row.pos[i / cols]);
         window.requestRedraw;
     }
     bool endCallback (ushort cx, ushort cy, ubyte b, bool state) {
@@ -309,7 +312,7 @@
 protected:
     // Data for resizing cols/rows:
     int dragX, dragY;	// coords where drag starts
-    //END Col/row resizing
+    //END Col/row resizing callback
     
     
     myIt cols, rows;	// number of cells in grid
@@ -334,6 +337,10 @@
         myDiff resizeD,	// resize down from this index (<0 if not resizing)
                resizeU;	// and up from this index
         int spacing;	// used by genPositions (which cannot access the layout class's data)
+        /* This is a delegate to a enclosing class's function, since:
+         * a different implementation is needed for cols or rows
+         * we're unable to access enclosing class members directly */
+        void delegate (myIt,int,int) setColWidth;	// set width of a column, with resize direction
         
         void dupMin () {
             width = minWidth.dup;
@@ -407,20 +414,25 @@
         *
         * Returns:
         *  The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.
+        *
+        * 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.
         */
-        int adjustCellSizes (int diff, myDiff start, myDiff incr)
-        in {// Could occur if adjust isn't called first, but this would be a code error:
-            char[128] tmp;
-            logger.trace (logger.format (tmp, "start is {}", start));
+        int adjustCellSizes (int diff, myDiff start, int incr)
+        in {
+            // Could occur if adjust isn't called first, but this would be a code error:
             assert (width !is null, "adjustCellSizes: width is null");
+            // Most likely if passed negative when sizing is disabled:
             assert (start >= 0 && start < minWidth.length, "adjustCellSizes: invalid start");
             assert (incr == 1 || incr == -1, "adjustCellSizes: invalid incr");
+            assert (setColWidth !is null, "adjustCellSizes: setColWidth is null");
         } body {
             debug scope(failure)
                     logger.trace ("adjustCellSizes: failure");
             myDiff i = start;
             if (diff > 0) {		// increase size of first resizable cell
                 width[i] += diff;
+                setColWidth (i, width[i], incr);
             }
             else if (diff < 0) {	// decrease
                 int rd = diff;		// running diff
@@ -428,13 +440,17 @@
                 while (true) {
                     width[i] += rd;	// decrease this cell's size (but may be too much)
                     rd = width[i] - minWidth[i];
-                    if (rd >= 0)	// OK; we're done
+                    if (rd >= 0) {	// OK; we're done
+                        setColWidth (i, width[i], incr);	// set new width
                         break;		// we hit the mark exactly: diff is correct
+                    }
                     
                     // else we decreased it too much!
                     width[i] = minWidth[i];
+                    setColWidth (i, width[i], incr);
                     // rd is remainder to decrease by
                     
+                    
                     bool it = true;	// iterate (force first time)
                     while (it) {
                         i += incr;
@@ -468,6 +484,8 @@
     CellDimensions col, row;
     
     // Index types. Note that in some cases they need to hold negative values.
+    // Int is used for resizing direction (although ptrdiff_t would be more appropriate),
+    // since the value must always be -1 or +1 and int is smaller on X86_64.
     alias size_t myIt;
     alias ptrdiff_t myDiff;
 }