Mercurial > projects > mde
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 }