diff mde/gui/widget/layout.d @ 43:1530d9c04d4d

Column/row resizing implemented for GridLayoutWidget (finally)! Also new exitImmediately option and a few debug scope(failure) log messages. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 15 May 2008 10:39:57 +0100
parents 8bf53e711cc7
children 0fd51d2c6c8a
line wrap: on
line diff
--- a/mde/gui/widget/layout.d	Wed May 14 12:31:09 2008 +0100
+++ b/mde/gui/widget/layout.d	Thu May 15 10:39:57 2008 +0100
@@ -19,6 +19,15 @@
 import mde.gui.widget.Widget;
 import mde.gui.exception;
 
+import tango.io.Stdout;
+debug {
+    import tango.util.log.Log : Log, Logger;
+    private Logger logger;
+    static this () {
+        logger = Log.getLogger ("mde.gui.widget.layout");
+    }
+}
+
 /** Encapsulates a grid of Widgets.
 *
 * Currently there is no support for changing number of cells, sub-widgets or sub-widget properties
@@ -58,14 +67,6 @@
         foreach (widget; subWidgets)
             data = widget.adjust (data);
         
-        // colWMin/rowHMin are needed as absolute quantities in this function:
-        int[] cwMin = new int[colWMin.length];
-        foreach (i,x; colWMin)
-            cwMin[i] = x>=0 ? x : -x;
-        int[] rhMin = new int[rowHMin.length];
-        foreach (i,x; rowHMin)
-            rhMin[i] = x>=0 ? x : -x;
-        
         /* We basically short-cut setSize by loading previous col/row sizes and doing the final
          * calculations.
          * Note: if setSize gets called afterwards, it should have same dimensions and so not do
@@ -73,8 +74,8 @@
         
         int lenUsed = 0;
         if (data.length < rows + cols) {    // data error; use defaults
-            colW = cwMin;
-            rowH = rhMin;
+            colW = colWMin.dup;
+            rowH = rowHMin.dup;
         } else {                            // sufficient data
             lenUsed = rows+cols;
             colW = data[0..cols];
@@ -84,9 +85,9 @@
             //NOTE: this could be made optional
             //NOTE: could also check non-resizable sizes are not too large
             foreach (i, ref w; colW)
-                if (w < cwMin[i]) w = cwMin[i];
+                if (w < colWMin[i]) w = colWMin[i];
             foreach (i, ref h; rowH)
-                if (h < rhMin[i]) h = rhMin[i];
+                if (h < rowHMin[i]) h = rowHMin[i];
         }
         
         genCachedMutableData;
@@ -117,18 +118,11 @@
     }
     
     bool isWSizable () {
-        // True if any column is resizable:
-        foreach (d; colWMin)
-            if (d >= 0)
-                return true;
-        return false;
+        return (sizableCols.length != 0);
     }
     
     bool isHSizable () {
-        foreach (d; rowHMin)
-            if (d >= 0)
-                return true;
-        return false;
+        return (sizableRows.length != 0);
     }
     
     /* Calculates the minimal size from all rows and columns of widgets. */
@@ -139,10 +133,10 @@
     
     void setSize (int nw, int nh) {
         // Step 1: calculate the row/column sizes.
-        w += adjustCellSizes (colW, colWMin, nw - w, true);
-        h += adjustCellSizes (rowH, rowHMin, nh - h, true);
+        w += adjustCellSizes (colW, colWMin, sizableCols, nw - w, true);
+        h += adjustCellSizes (rowH, rowHMin, sizableRows, nh - h, true);
         
-        // Step 2: calculate the row/column offsets (positions) and set the sub-widgets sizes.
+        // Step 2: calculate the row/column offsets (positions) and set the sub-widget's sizes.
         genCachedMutableData;
         
         // Step 3: position needs to be set
@@ -165,7 +159,7 @@
         // Find the column
         int i = cols - 1;                   // starting from right...
         while (lx < colX[i]) {              // decrement while left of this column
-            assert (i > 0, "getWidget: left of first column");  // should be impossible
+            debug assert (i > 0, "getWidget: left of first column");  // should be impossible
             --i;
         }                                   // now (lx >= colX[i])
         if (lx >= colX[i] + colW[i]) return this;   // between columns
@@ -173,7 +167,7 @@
         // Find the row;
         int j = rows - 1;
         while (ly < rowY[j]) {
-            assert (j > 0, "getWidget: above first row");   // should be impossible
+            debug assert (j > 0, "getWidget: above first row");   // should be impossible
             --j;
         }
         if (ly >= rowY[j] + rowH[j]) return this;
@@ -190,28 +184,59 @@
     
     // Resizing columns & rows
     void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {
+        debug scope (failure)
+                logger.warn ("clickEvent: failure");
         if (b == 1 && state == true) {
-            int l = cx - x;    // use relative coords
+            /* Note: Because of getWidget, this function is only called if the click is not on a
+            * sub-widget, so we know it's on some divisor (so at least one of resizeCol and
+            * resizeRow is non-negative). */
             
             // Find the column
-            resizeCol = cols - 1;           // from the right...
-            while (l < colX[resizeCol]) {   // decrement while left of this column
-                assert (resizeCol > 0, "clickEvent: left of first column");
-                --resizeCol;
-            }                               // now (l >= colX[resizeCol])
-            if (l < colX[resizeCol] + colW[resizeCol]) resizeCol = -1;  // on a sub-widget
+            if (sizableCols.length != 0) {
+                int l = cx - x;                 // use relative coords
+                size_t i = cols - 1;            // index, from right
+                while (l < colX[i]) {           // decrement while left of this column
+                    debug assert (i > 0, "clickEvent: left of first column");
+                    --i;
+                }                               // now (l >= colX[resizeCol])
+                if (l < colX[i] + colW[i]) i = -1;  // on a sub-widget
+                
+                // Set resizeColsL / resizeColsH
+                // Want to find j such that [0..j],[j..$] divide sizableCols about i:
+                size_t j = 0;
+                while (j < sizableCols.length && sizableCols[j] <= i) ++j;
+                
+                resizeColsL = sizableCols[0..j];
+                resizeColsH = sizableCols[j..$];
+                
+                // Cannot resize if either list is empty. resizeCallback checks the length of L,
+                // but to save it checking R too, we set L's length zero if R's is.
+                if (resizeColsH.length == 0)
+                    resizeColsL = null;
+            }
             
             // Find the row
-            resizeRow = rows - 1;
-            while (l < rowY[resizeRow]) {
-                assert (resizeRow > 0, "clickEvent: left of first column");
-                --resizeRow;
+            if (sizableRows.length != 0) {
+                int l = cy - y;
+                size_t i = rows - 1;
+                while (l < rowY[i]) {
+                    debug assert (i > 0, "clickEvent: above first row");
+                    --i;
+                }
+                if (l < rowY[i] + rowH[i]) i = -1;
+                
+                size_t j = 0;
+                while (j < sizableRows.length && sizableRows[j] <= i) ++j;
+                
+                resizeRowsL = sizableRows[0..j];
+                resizeRowsH = sizableRows[j..$];
+                
+                if (resizeRowsH.length == 0)
+                    resizeRowsL = null;
             }
-            if (l < rowY[resizeRow] + rowH[resizeRow]) resizeRow = -1;  // on a sub-widget
             
-            /* Note: Because of getWidget, this function is only called if the click is not on a
-             * sub-widget, so we know it's on some divisor (so at least one of resizeCol and
-             * resizeRow is non-negative). */
+            if (resizeColsL is null && resizeRowsL is null)
+                return;     // no resizing to do
             
             dragX = cx;
             dragY = cy;
@@ -260,26 +285,25 @@
         
         
         // Find which cols/rows are resizable:
+        sizableCols = sizableRows = null;   // reset; we're about to concatenate to them
+        
         forCols:
         for (uint i = 0; i < cols; ++i) {                       // for each column
             for (uint j = 0; j < subWidgets.length; j += cols)  // for each row
-                if (!subWidgets[i+j].isWSizable) {      // column not resizable
-                    colWMin[i] = -colWMin[i];           // negate to show not resizable
+                if (!subWidgets[i+j].isWSizable)        // column not resizable
                     continue forCols;                   // continue the outer for loop
-                }
                 
             // column is resizable if we get to here
-            // entry is left positive, meaning it is resizable
+            sizableCols ~= i;
         }
         
         forRows:
         for (uint i = 0; i < subWidgets.length; i += cols) {    // for each row
             for (uint j = 0; j < cols; ++j)                     // for each column
-                if (!subWidgets[i+j].isHSizable) {
-                    j = i / cols;       // reuse: will be reinitialised next iteration
-                    rowHMin[j] = -rowHMin[j];
+                if (!subWidgets[i+j].isHSizable)
                     continue forRows;
-                }
+            
+            sizableRows ~= i / cols;
         }
     }
     
@@ -314,50 +338,46 @@
      * Params:
      *  cellD       = current sizes; is adjusted by the function to new sizes
      *  cellDMin    = minimal sizes (see colWMin/rowHMin)
+     *  sizableCells= List of indexes in cellD for cells which are resizable
      *  diff        = amount to increase/decrease the total size
      *  startHigh   = if true, start resizing from the tail end of cellD, instead of the beginning
      *
      * Returns:
      *  The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.
      */
-    int adjustCellSizes (ref int[] cellD, ref int[] cellDMin, int diff, bool startHigh)
+    int adjustCellSizes (ref int[] cellD, ref int[] cellDMin, ref size_t[] sizableCells, int diff, bool startHigh)
     in {// Could occur if adjust isn't called first, but this would be a code error:
         assert (cellD !is null, "adjustCellSizes: cellD is null");
     } body {
-        size_t i = (startHigh ? cellD.length-1 : 0);
-        size_t ic = (startHigh ? -1 : 1);   // amount to iterate to next cell
+        // Cannot resize if no cells are sizable:
+        if (sizableCells.length == 0) return 0;
+        
+        size_t si = (startHigh ? sizableCells.length-1 : 0);    // starting index of sizableCells
         
         // FIXME: could do with an overhaul
         if (diff > 0) {         // increase size of first resizable cell
-            do {
-                if (cellDMin[i] >= 0) { // cell is resizable
-                    cellD[i] += diff;
-                    return diff;
-                }
-                i += ic;
-            } while (i >= 0 && i < cellD.length);
-            // no resizable cell: revert to original
-            cellD[(startHigh ? cellD.length-1 : 0)] += diff;
+            cellD[sizableCells[si]] += diff;
             return diff;
         }
         else if (diff < 0) {    // decrease
+            size_t sc = (startHigh ? -1 : 1);   // amount to iterate
+            size_t ci;          // index in cellD
             int rd = diff;      // running diff
             while (true) {
-                // NOTE: could do this differently so that enlarged cells can be shrunk
-                if (cellDMin[i] >= 0) { // i.e., if resizable:
-                    cellD[i] += rd; // decrease this cell's size (but may be too much)
-                    rd = cellD[i] - cellDMin[i];
-                    if (rd >= 0)    // OK; we're done
-                        return diff;// we hit the mark exactly
-                    
-                    // else we decreased it too much!
-                    cellD[i] = cellDMin[i];
-                    // rd is remainder to decrease by
-                }
+                ci = sizableCells[si];
+                
+                cellD[ci] += rd; // decrease this cell's size (but may be too much)
+                rd = cellD[ci] - cellDMin[ci];
+                if (rd >= 0)    // OK; we're done
+                    return diff;// we hit the mark exactly
                 
-                i += (startHigh ? -1 : 1);  // next cell
-                if (i < 0 || i >= cellD.length) // run out of next cells
-                    return diff - rd;       // still had rd left to decrease
+                // else we decreased it too much!
+                cellD[ci] = cellDMin[ci];
+                // rd is remainder to decrease by
+                
+                si += sc;       // iterate
+                if (si < 0 || si >= sizableCells.length)        // run out of next cells
+                    return diff - rd;   // still had rd left to decrease
             }
         }
         else                   // no adjustment needed
@@ -368,30 +388,48 @@
     void resizeCallback (ushort cx, ushort cy) {
         int move;           // NOTE: all resizing is relative, unlike in Window
         
-        if (resizeCol >= 0) {
+        if (resizeColsL.length != 0) {
             move = cx - dragX;
-            int[] d0 = colW[0..resizeCol];
-            int[] d1 = colW[resizeCol..$];
-            int[] m0 = colWMin[0..resizeCol];
-            int[] m1 = colWMin[resizeCol..$];
             
             // do shrinking first (in case we hit the minimum)
             // Note: we rely on x[a..b] pointing to the same memory as x
-            if (move > 0) {
-                move = -adjustCellSizes (d1, m1, -move, false);
-                adjustCellSizes (d0, m0, move, true);
+            if (move < 0) {
+                move = -adjustCellSizes (colW, colWMin, resizeColsL, move, false);
+                adjustCellSizes (colW, colWMin, resizeColsH, move, true);
             } else {
-                move = -adjustCellSizes (d0, m0, move, true);
-                adjustCellSizes (d1, m1, move, false);
+                move = -adjustCellSizes (colW, colWMin, resizeColsH, -move, true);
+                adjustCellSizes (colW, colWMin, resizeColsL, move, false);
             }
+            
+            dragX = cx;
         }
-        // FIXME: vertical
         
+        if (resizeRowsL.length != 0) {
+            move = cy - dragY;
+            
+            // do shrinking first (in case we hit the minimum)
+            // Note: we rely on x[a..b] pointing to the same memory as x
+            if (move < 0) {
+                move = -adjustCellSizes (rowH, rowHMin, resizeRowsL, move, false);
+                adjustCellSizes (rowH, rowHMin, resizeRowsH, move, true);
+            } else {
+                move = -adjustCellSizes (rowH, rowHMin, resizeRowsH, -move, true);
+                adjustCellSizes (rowH, rowHMin, resizeRowsL, move, false);
+            }
+            
+            dragY = cy;
+        }
+        
+        // NOTE: this might be able to be made more efficient, but basically this all needs to happen:
+        genCachedMutableData;
+        setPosition (x,y);
         window.requestRedraw;
     }
     bool endCallback (ushort cx, ushort cy, ubyte b, bool state) {
         if (b == 1 && state == false) {
             window.gui.removeCallbacks (cast(void*) this);
+            // Remove unwanted data (although it shouldn't free any memory):
+            resizeColsL = resizeColsH = resizeRowsL = resizeRowsH = null;
             return true;    // we've handled the up-click
         }
         return false;       // we haven't handled it
@@ -399,27 +437,33 @@
     
 protected:
     // Data for resizing cols/rows:
-    int resizeCol = -1, // col/row being resized;
-        resizeRow = -1; // negative if none
     int dragX, dragY;   // coords where drag starts
+    // Lists of columns/rows with lower/higher index than the resize position
+    size_t[] resizeColsL, resizeColsH, resizeRowsL, resizeRowsH;
     //END Col/row resizing
     
-    // Construction data (saved):
+    
+    //BEGIN Construction (i.e. non-mutable) data
     int cols, rows;     // number of cells in grid
-    IWidget[] subWidgets;   // all widgets in the grid (by row):
-    /* SubWidget order:    [ 0 1 ]
-    *                      [ 2 3 ] */
+    
+    /* All widgets in the grid, by row. Order:  [ 0 1 ]
+     *                                          [ 2 3 ] */
+    IWidget[] subWidgets;
     
     // Cached data calculated from construction data:
-    int[] colWMin;      // absolute value is minimal column width / row height
-    int[] rowHMin;      // negative if fixed size, entry >= 0 if resizible
+    // Minimal column width / row height:
+    int[] colWMin, rowHMin;
     int mw, mh;         // minimal dimensions
     
-    // Mutable data (saved):
-    int[] colW;         // column width (widest widget)
-    int[] rowH;         // row height (highest widget in the row)
+    // All resizable cols/rows, in order:
+    size_t[] sizableCols, sizableRows;
+    //END Construction data
+    
+    
+    //BEGIN Mutable data
+    int[] colW, rowH;   // column width / row height (largest size in col/row)
     
     // Cached data calculated from construction and mutable data:
-    int[] colX;         // cumulative colW[i-1] + padding (add x to get column's left x-coord)
-    int[] rowY;         // cumulative rowH[i-1] + padding
+    int[] colX, rowY;   // cumulative colW[i-1] + padding (add x to get column's left x-coord)
+    //END Mutable data
 }