# HG changeset patch # User Diggory Hardy # Date 1210764669 -3600 # Node ID 8bf53e711cc7201031091607700cb9586f5b3523 # Parent b3a6ca4516b42d1583b43e7673bf1eac22d3caa6 Partially implemented column/row resizing (code not working well). Committing before reverting/deleting many edits. committer: Diggory Hardy diff -r b3a6ca4516b4 -r 8bf53e711cc7 mde/gui/widget/layout.d --- 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)