changeset 42:8bf53e711cc7

Partially implemented column/row resizing (code not working well). Committing before reverting/deleting many edits. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Wed, 14 May 2008 12:31:09 +0100
parents b3a6ca4516b4
children 1530d9c04d4d
files mde/gui/widget/layout.d
diffstat 1 files changed, 160 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/mde/gui/widget/layout.d	Tue May 13 12:02:36 2008 +0100
+++ b/mde/gui/widget/layout.d	Wed May 14 12:31:09 2008 +0100
@@ -58,6 +58,14 @@
         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
@@ -65,8 +73,8 @@
         
         int lenUsed = 0;
         if (data.length < rows + cols) {    // data error; use defaults
-            colW = colWMin.dup;
-            rowH = rowHMin.dup;
+            colW = cwMin;
+            rowH = rhMin;
         } else {                            // sufficient data
             lenUsed = rows+cols;
             colW = data[0..cols];
@@ -74,10 +82,11 @@
             
             // Check row sizes are valid:
             //NOTE: this could be made optional
+            //NOTE: could also check non-resizable sizes are not too large
             foreach (i, ref w; colW)
-                if (w < colWMin[i]) w = colWMin[i];
+                if (w < cwMin[i]) w = cwMin[i];
             foreach (i, ref h; rowH)
-                if (h < rowHMin[i]) h = rowHMin[i];
+                if (h < rhMin[i]) h = rhMin[i];
         }
         
         genCachedMutableData;
@@ -108,11 +117,18 @@
     }
     
     bool isWSizable () {
-        return (sizableCols.length != 0);
+        // True if any column is resizable:
+        foreach (d; colWMin)
+            if (d >= 0)
+                return true;
+        return false;
     }
     
     bool isHSizable () {
-        return (sizableRows.length != 0);
+        foreach (d; rowHMin)
+            if (d >= 0)
+                return true;
+        return false;
     }
     
     /* Calculates the minimal size from all rows and columns of widgets. */
@@ -123,8 +139,8 @@
     
     void setSize (int nw, int nh) {
         // Step 1: calculate the row/column sizes.
-        setSizeImpl!(true)  (nw);
-        setSizeImpl!(false) (nh);
+        w += adjustCellSizes (colW, colWMin, nw - w, true);
+        h += adjustCellSizes (rowH, rowHMin, nh - h, true);
         
         // Step 2: calculate the row/column offsets (positions) and set the sub-widgets sizes.
         genCachedMutableData;
@@ -149,7 +165,7 @@
         // Find the column
         int i = cols - 1;                   // starting from right...
         while (lx < colX[i]) {              // decrement while left of this column
-            if (i == 0) return this;        // left of first column
+            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
@@ -157,7 +173,7 @@
         // Find the row;
         int j = rows - 1;
         while (ly < rowY[j]) {
-            if (j == 0) return this;
+            assert (j > 0, "getWidget: above first row");   // should be impossible
             --j;
         }
         if (ly >= rowY[j] + rowH[j]) return this;
@@ -172,6 +188,39 @@
         return this;    // wasn't in cell
     }
     
+    // Resizing columns & rows
+    void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {
+        if (b == 1 && state == true) {
+            int l = cx - x;    // use relative coords
+            
+            // 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
+            
+            // Find the row
+            resizeRow = rows - 1;
+            while (l < rowY[resizeRow]) {
+                assert (resizeRow > 0, "clickEvent: left of first column");
+                --resizeRow;
+            }
+            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). */
+            
+            dragX = cx;
+            dragY = cy;
+            
+            window.gui.addClickCallback (&endCallback);
+            window.gui.addMotionCallback (&resizeCallback);
+        }
+    }
+    
     void draw () {
         super.draw ();
         
@@ -180,6 +229,7 @@
     }
     
 private:
+    //BEGIN Cache calculation functions
     /* Calculations which need to be run whenever a new sub-widget structure is set
      * (i.e. to produce cached data calculated from construction data). */
     void genCachedConstructionData () {
@@ -210,26 +260,26 @@
         
         
         // Find which cols/rows are resizable:
-        sizableCols = null;     // clear these because we append to them
-        sizableRows = null;
-        
         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
+                if (!subWidgets[i+j].isWSizable) {      // column not resizable
+                    colWMin[i] = -colWMin[i];           // negate to show not resizable
                     continue forCols;                   // continue the outer for loop
+                }
                 
             // column is resizable if we get to here
-            sizableCols ~= i;
+            // entry is left positive, meaning it is resizable
         }
         
         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)
+                if (!subWidgets[i+j].isHSizable) {
+                    j = i / cols;       // reuse: will be reinitialised next iteration
+                    rowHMin[j] = -rowHMin[j];
                     continue forRows;
-                
-            sizableRows ~= i / cols;    // the current row
+                }
         }
     }
     
@@ -257,61 +307,103 @@
         foreach (i,widget; subWidgets)
             widget.setSize (colW[i % cols], rowH[i / cols]);
     }
+    //END Cache calculation functions
     
-    /* setSize generalised for either dimension; w/h is renamed d, col/row renamed cell. */
-    void setSizeImpl(bool W) (int nd) {
-        static if (W) {
-            alias w         d;
-            alias mw        md;
-            alias colW      cellD;
-            alias colWMin   cellDMin;
-            alias sizableCols sizableCells;
-        } else {
-            alias h         d;
-            alias mh        md;
-            alias rowH      cellD;
-            alias rowHMin   cellDMin;
-            alias sizableRows sizableCells;
-        }
-        // Could occur if adjust isn't called first, but this would be a code error:
-        assert (cellD !is null, "setSizeImpl: cellD is null");
+    /* Adjust the total size of rows/columns (including spacing) by diff.
+     *
+     * Params:
+     *  cellD       = current sizes; is adjusted by the function to new sizes
+     *  cellDMin    = minimal sizes (see colWMin/rowHMin)
+     *  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)
+    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
         
-        /* For each of width and height, there are several cases:
-        *  [new value is] more than old value
-        *  ->  enlarge any row/column
-        *  same as old value
-        *  ->  do nothing
-        *  more than min but less than current value
-        *  ->  find an enlarged row/col and reduce size
-        *  ->  repeat if necessary
-        *  minimal value or less
-        *  -> clamp to min and use min col/row sizes
-        */
-        if (nd > d) {       // expand (d < nd)
-            if (sizableCells.length) {               // check there is a resizable col/row
-                cellD[sizableCells[$-1]] += nd - d;  // new size
-                d = nd;
+        // 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;
+            return diff;
+        }
+        else if (diff < 0) {    // decrease
+            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
+                }
+                
+                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 if (nd < d) {
-            if (nd > md) {  // shrink (md < nd < d)
-                int toReduce = nd - d;  // negative
-                foreach_reverse (cell; sizableCells) {
-                    cellD[cell] += toReduce;
-                    toReduce = cellD[cell] - cellDMin[cell];
-                    if (toReduce < 0)   // reduced too far
-                        cellD[cell] = cellDMin[cell];
-                    else                // ok; cellD >= cellDMin
-                        break;
-                }
-                d = nd;
-            } else {        // clamp  (nd <= md)
-                cellD = cellDMin.dup;   // duplicate so future edits don't affect cellDMin
-                d = md;
+        }
+        else                   // no adjustment needed
+            return 0;
+    }
+    
+    //BEGIN Col/row resizing
+    void resizeCallback (ushort cx, ushort cy) {
+        int move;           // NOTE: all resizing is relative, unlike in Window
+        
+        if (resizeCol >= 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);
+            } else {
+                move = -adjustCellSizes (d0, m0, move, true);
+                adjustCellSizes (d1, m1, move, false);
             }
-        }                   // third possibility: nd = d
+        }
+        // FIXME: vertical
+        
+        window.requestRedraw;
+    }
+    bool endCallback (ushort cx, ushort cy, ubyte b, bool state) {
+        if (b == 1 && state == false) {
+            window.gui.removeCallbacks (cast(void*) this);
+            return true;    // we've handled the up-click
+        }
+        return false;       // we haven't handled it
     }
     
 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
+    //END Col/row resizing
+    
     // Construction data (saved):
     int cols, rows;     // number of cells in grid
     IWidget[] subWidgets;   // all widgets in the grid (by row):
@@ -319,13 +411,10 @@
     *                      [ 2 3 ] */
     
     // Cached data calculated from construction data:
-    int[] colWMin;      // minimal column width
-    int[] rowHMin;      // minimal row height
+    int[] colWMin;      // absolute value is minimal column width / row height
+    int[] rowHMin;      // negative if fixed size, entry >= 0 if resizible
     int mw, mh;         // minimal dimensions
     
-    int[] sizableCols;   // all resizable columns / rows, empty if not resizable
-    int[] sizableRows;   // resizing is done from last entry
-    
     // Mutable data (saved):
     int[] colW;         // column width (widest widget)
     int[] rowH;         // row height (highest widget in the row)