comparison mde/gui/widget/layout.d @ 45:0fd51d2c6c8a

Several changes to resising windows and layout widgets. This commit still has some bugs. 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). committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 22 May 2008 11:34:09 +0100
parents 1530d9c04d4d
children 03fa79a48c48
comparison
equal deleted inserted replaced
44:07bd1a09e161 45:0fd51d2c6c8a
17 module mde.gui.widget.layout; 17 module mde.gui.widget.layout;
18 18
19 import mde.gui.widget.Widget; 19 import mde.gui.widget.Widget;
20 import mde.gui.exception; 20 import mde.gui.exception;
21 21
22 import tango.io.Stdout;
23 debug { 22 debug {
24 import tango.util.log.Log : Log, Logger; 23 import tango.util.log.Log : Log, Logger;
25 private Logger logger; 24 private Logger logger;
26 static this () { 25 static this () {
27 logger = Log.getLogger ("mde.gui.widget.layout"); 26 logger = Log.getLogger ("mde.gui.widget.layout");
45 super (wind, data); 44 super (wind, data);
46 45
47 rows = data[1]; 46 rows = data[1];
48 cols = data[2]; 47 cols = data[2];
49 if (data.length != 3 + rows * cols) throw new WidgetDataException; 48 if (data.length != 3 + rows * cols) throw new WidgetDataException;
50 /* data.length >= 3 so besides checking the length is correct, this tells us: 49 /* data.length >= 4 so besides checking the length is correct, this tells us:
51 * rows * cols >= 4 - 3 = 1 a free check! 50 * rows * cols >= 4 - 3 = 1 a free check!
52 * The only thing not checked is whether both rows and cols are negative, which would 51 * The only thing not checked is whether both rows and cols are negative, which would
53 * cause an exception when dynamic arrays are allocated later (and is unlikely). */ 52 * cause an exception when dynamic arrays are allocated by genCachedConstructionData, which
53 * is an acceptible method of failure (and is unlikely anyway). */
54 54
55 // Get all sub-widgets 55 // Get all sub-widgets
56 subWidgets.length = rows*cols; 56 subWidgets.length = rows*cols;
57 foreach (i, ref subWidget; subWidgets) { 57 foreach (i, ref subWidget; subWidgets) {
58 subWidget = window.makeWidget (data[i+3]); 58 subWidget = window.makeWidget (data[i+3]);
72 * Note: if setSize gets called afterwards, it should have same dimensions and so not do 72 * Note: if setSize gets called afterwards, it should have same dimensions and so not do
73 * anything. */ 73 * anything. */
74 74
75 int lenUsed = 0; 75 int lenUsed = 0;
76 if (data.length < rows + cols) { // data error; use defaults 76 if (data.length < rows + cols) { // data error; use defaults
77 colW = colWMin.dup; 77 col.dupMin;
78 rowH = rowHMin.dup; 78 row.dupMin;
79 } else { // sufficient data 79 } else { // sufficient data
80 lenUsed = rows+cols; 80 lenUsed = rows+cols;
81 colW = data[0..cols]; 81 col.setCheck (data[0..cols]);
82 rowH = data[cols..lenUsed]; 82 row.setCheck (data[cols..lenUsed]);
83 83 }
84 // Check row sizes are valid: 84
85 //NOTE: this could be made optional 85
86 //NOTE: could also check non-resizable sizes are not too large 86 // Generate cached mutable data
87 foreach (i, ref w; colW) 87 // Calculate column and row locations:
88 if (w < colWMin[i]) w = colWMin[i]; 88 w = col.genPositions;
89 foreach (i, ref h; rowH) 89 h = row.genPositions;
90 if (h < rowHMin[i]) h = rowHMin[i]; 90
91 } 91 // Tell subwidgets their new sizes. Positions are given by a later call to setPosition.
92 92 foreach (i,widget; subWidgets)
93 genCachedMutableData; 93 // Resizing direction is arbitrarily set to "high direction":
94 w = colW[$-1] + colX[$-1]; 94 widget.setSize (col.width[i % cols], row.width[i / cols], true, true);
95 h = rowY[$-1] + rowH[$-1];
96 95
97 return data[lenUsed..$]; 96 return data[lenUsed..$];
98 } 97 }
99 98
100 int[] getCreationData () { 99 int[] getCreationData () {
111 int[] getMutableData () { 110 int[] getMutableData () {
112 int[] ret; 111 int[] ret;
113 foreach (widget; subWidgets) 112 foreach (widget; subWidgets)
114 ret ~= widget.getMutableData; 113 ret ~= widget.getMutableData;
115 114
116 ret ~= colW ~ rowH; 115 ret ~= col.width ~ row.width;
117 return ret; 116 return ret;
118 } 117 }
119 118
120 bool isWSizable () { 119 bool isWSizable () {
121 return (sizableCols.length != 0); 120 return col.firstSizable >= 0;
122 } 121 }
123
124 bool isHSizable () { 122 bool isHSizable () {
125 return (sizableRows.length != 0); 123 return row.firstSizable >= 0;
126 } 124 }
127 125
128 /* Calculates the minimal size from all rows and columns of widgets. */ 126 /* Calculates the minimal size from all rows and columns of widgets. */
129 void getMinimalSize (out int mw, out int mh) { 127 void getMinimalSize (out int mw, out int mh) {
130 mw = this.mw; 128 mw = this.mw;
131 mh = this.mh; 129 mh = this.mh;
132 } 130 }
133 131
134 void setSize (int nw, int nh) { 132 void setSize (int nw, int nh, bool wHigh, bool hHigh) {
135 // Step 1: calculate the row/column sizes. 133 debug scope (failure) {
136 w += adjustCellSizes (colW, colWMin, sizableCols, nw - w, true); 134 char[128] tmp;
137 h += adjustCellSizes (rowH, rowHMin, sizableRows, nh - h, true); 135 logger.trace ("setSize failed: hHigh = " ~ (hHigh ? "true" : "false"));
138 136 logger.trace (logger.format (tmp, "rows to resize: {}, {}", row.firstSizable, row.lastSizable));
139 // Step 2: calculate the row/column offsets (positions) and set the sub-widget's sizes. 137 }
140 genCachedMutableData; 138 // Optimisation (could easily be called with same sizes if a parent layout widget is
141 139 // resized, since many columns/rows may not be resized).
142 // Step 3: position needs to be set 140 if (nw == w && nh == h) return;
143 // Currently this happens by specifying that setPosition should be run after setSize. 141
142 // calculate the row/column sizes (and new positions)
143 if (wHigh)
144 w += col.adjustCellSizes (nw - w, col.lastSizable, -1);
145 else
146 w += col.adjustCellSizes (nw - w, col.firstSizable, 1);
147 if (hHigh)
148 h += row.adjustCellSizes (nh - h, row.lastSizable, -1);
149 else
150 h += row.adjustCellSizes (nh - h, row.firstSizable, 1);
151
152 // set the sub-widget's sizes & positions
153 setSubWidgetSP (wHigh, hHigh);
144 } 154 }
145 155
146 void setPosition (int x, int y) { 156 void setPosition (int x, int y) {
147 this.x = x; 157 this.x = x;
148 this.y = y; 158 this.y = y;
149 159
150 foreach (i,widget; subWidgets) 160 foreach (i,widget; subWidgets)
151 widget.setPosition (x + colX[i % cols], y + rowY[i / cols]); 161 widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]);
152 } 162 }
153 163
154 164
155 // Find the relevant widget. 165 // Find the relevant widget.
156 IWidget getWidget (int cx, int cy) { 166 IWidget getWidget (int cx, int cy) {
157 int lx = cx - x, ly = cy - y; // use coords relative to this widget 167 debug scope (failure)
158 168 logger.warn ("getWidget: failure");
159 // Find the column 169 // Find row/column:
160 int i = cols - 1; // starting from right... 170 myDiff i = col.getCell (cx - x);
161 while (lx < colX[i]) { // decrement while left of this column 171 myDiff j = row.getCell (cy - y);
162 debug assert (i > 0, "getWidget: left of first column"); // should be impossible 172 if (i < 0 || j < 0) // on a space between widgets
163 --i; 173 return this;
164 } // now (lx >= colX[i]) 174
165 if (lx >= colX[i] + colW[i]) return this; // between columns 175 // On a subwidget; recurse call:
166 176 return subWidgets[i + j*cols].getWidget (cx, cy);
167 // Find the row;
168 int j = rows - 1;
169 while (ly < rowY[j]) {
170 debug assert (j > 0, "getWidget: above first row"); // should be impossible
171 --j;
172 }
173 if (ly >= rowY[j] + rowH[j]) return this;
174
175 // Now we know it's in widget (i,j)'s cell (but the widget may not take up the whole cell)
176 lx -= colX[i];
177 ly -= rowY[j];
178 IWidget widg = subWidgets[i + j*cols];
179 widg.getCurrentSize (i,j);
180 if (lx < i && ly < j)
181 return widg.getWidget (cx, cy);
182 return this; // wasn't in cell
183 } 177 }
184 178
185 // Resizing columns & rows 179 // Resizing columns & rows
186 void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 180 void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {
187 debug scope (failure) 181 debug scope (failure)
189 if (b == 1 && state == true) { 183 if (b == 1 && state == true) {
190 /* Note: Because of getWidget, this function is only called if the click is not on a 184 /* Note: Because of getWidget, this function is only called if the click is not on a
191 * sub-widget, so we know it's on some divisor (so at least one of resizeCol and 185 * sub-widget, so we know it's on some divisor (so at least one of resizeCol and
192 * resizeRow is non-negative). */ 186 * resizeRow is non-negative). */
193 187
194 // Find the column 188 // find col/row's resizeD & resizeU
195 if (sizableCols.length != 0) { 189 if (col.findResize (cx - x) && row.findResize (cy - y))
196 int l = cx - x; // use relative coords 190 return; // unable to resize
197 size_t i = cols - 1; // index, from right
198 while (l < colX[i]) { // decrement while left of this column
199 debug assert (i > 0, "clickEvent: left of first column");
200 --i;
201 } // now (l >= colX[resizeCol])
202 if (l < colX[i] + colW[i]) i = -1; // on a sub-widget
203
204 // Set resizeColsL / resizeColsH
205 // Want to find j such that [0..j],[j..$] divide sizableCols about i:
206 size_t j = 0;
207 while (j < sizableCols.length && sizableCols[j] <= i) ++j;
208
209 resizeColsL = sizableCols[0..j];
210 resizeColsH = sizableCols[j..$];
211
212 // Cannot resize if either list is empty. resizeCallback checks the length of L,
213 // but to save it checking R too, we set L's length zero if R's is.
214 if (resizeColsH.length == 0)
215 resizeColsL = null;
216 }
217
218 // Find the row
219 if (sizableRows.length != 0) {
220 int l = cy - y;
221 size_t i = rows - 1;
222 while (l < rowY[i]) {
223 debug assert (i > 0, "clickEvent: above first row");
224 --i;
225 }
226 if (l < rowY[i] + rowH[i]) i = -1;
227
228 size_t j = 0;
229 while (j < sizableRows.length && sizableRows[j] <= i) ++j;
230
231 resizeRowsL = sizableRows[0..j];
232 resizeRowsH = sizableRows[j..$];
233
234 if (resizeRowsH.length == 0)
235 resizeRowsL = null;
236 }
237
238 if (resizeColsL is null && resizeRowsL is null)
239 return; // no resizing to do
240 191
241 dragX = cx; 192 dragX = cx;
242 dragY = cy; 193 dragY = cy;
243 194
244 window.gui.addClickCallback (&endCallback); 195 window.gui.addClickCallback (&endCallback);
256 private: 207 private:
257 //BEGIN Cache calculation functions 208 //BEGIN Cache calculation functions
258 /* Calculations which need to be run whenever a new sub-widget structure is set 209 /* Calculations which need to be run whenever a new sub-widget structure is set
259 * (i.e. to produce cached data calculated from construction data). */ 210 * (i.e. to produce cached data calculated from construction data). */
260 void genCachedConstructionData () { 211 void genCachedConstructionData () {
212 col.spacing = row.spacing = window.renderer.layoutSpacing;
213
261 // Calculate the minimal column and row sizes: 214 // Calculate the minimal column and row sizes:
262 colWMin = new int[cols]; // set length, making sure the arrays are initialised to zero 215 // set length, making sure the arrays are initialised to zero:
263 rowHMin = new int[rows]; 216 col.minWidth = new int[cols];
217 row.minWidth = new int[rows];
264 int ww, wh; // sub-widget minimal sizes 218 int ww, wh; // sub-widget minimal sizes
265 foreach (i,widget; subWidgets) { 219 foreach (i,widget; subWidgets) {
266 widget.getMinimalSize (ww, wh); 220 widget.getMinimalSize (ww, wh);
267 221
268 // Increase dimensions if current minimal size is larger: 222 // Increase dimensions if current minimal size is larger:
269 uint n = i % cols; // column 223 myIt n = i % cols; // column
270 if (colWMin[n] < ww) colWMin[n] = ww; 224 if (col.minWidth[n] < ww) col.minWidth[n] = ww;
271 n = i / cols; // row 225 n = i / cols; // row
272 if (rowHMin[n] < wh) rowHMin[n] = wh; 226 if (row.minWidth[n] < wh) row.minWidth[n] = wh;
273 } 227 }
274 228
275 229
276 // Calculate the overall minimal size, starting with the spacing: 230 // Calculate the overall minimal size, starting with the spacing:
277 mh = window.renderer.layoutSpacing; // use mh temporarily 231 mh = window.renderer.layoutSpacing; // use mh temporarily
278 mw = mh * (cols - 1); 232 mw = mh * (cols - 1);
279 mh *= (rows - 1); 233 mh *= (rows - 1);
280 234
281 foreach (x; colWMin) // add the column/row's dimensions 235 foreach (x; col.minWidth) // add the column/row's dimensions
282 mw += x; 236 mw += x;
283 foreach (x; rowHMin) 237 foreach (x; row.minWidth)
284 mh += x; 238 mh += x;
285 239
286 240
287 // Find which cols/rows are resizable: 241 // Find which cols/rows are resizable:
288 sizableCols = sizableRows = null; // reset; we're about to concatenate to them 242 // reset:
243 col.sizable = new bool[cols];
244 row.sizable = new bool[rows];
245 col.firstSizable = row.firstSizable = -1;
289 246
290 forCols: 247 forCols:
291 for (uint i = 0; i < cols; ++i) { // for each column 248 for (myIt i = 0; i < cols; ++i) { // for each column
292 for (uint j = 0; j < subWidgets.length; j += cols) // for each row 249 for (myIt j = 0; j < subWidgets.length; j += cols) // for each row
293 if (!subWidgets[i+j].isWSizable) // column not resizable 250 if (!subWidgets[i+j].isWSizable) // column not resizable
294 continue forCols; // continue the outer for loop 251 continue forCols; // continue the outer for loop
295 252
296 // column is resizable if we get to here 253 // column is resizable if we get to here
297 sizableCols ~= i; 254 col.sizable[i] = true;
255 if (col.firstSizable < 0)
256 col.firstSizable = i;
257 col.lastSizable = i;
298 } 258 }
299 259
300 forRows: 260 forRows:
301 for (uint i = 0; i < subWidgets.length; i += cols) { // for each row 261 for (myIt i = 0; i < subWidgets.length; i += cols) { // for each row
302 for (uint j = 0; j < cols; ++j) // for each column 262 for (myIt j = 0; j < cols; ++j) // for each column
303 if (!subWidgets[i+j].isHSizable) 263 if (!subWidgets[i+j].isHSizable)
304 continue forRows; 264 continue forRows;
305 265
306 sizableRows ~= i / cols; 266 row.lastSizable = i / cols;
307 } 267 row.sizable[row.lastSizable] = true;
308 } 268 if (row.firstSizable < 0)
309 269 row.firstSizable = row.lastSizable;
310 /* Calculations which need to be run whenever resizing occurs (or deeper alterations) 270 }
311 * (i.e. to produce cached data calculated from construction and mutable data). */ 271 }
312 void genCachedMutableData () { 272
313 // Calculate column and row locations: 273 // set sub-widgets size & position (done after resizing widget or rows/columns)
314 colX.length = cols; 274 void setSubWidgetSP (bool wH, bool hH) {
315 rowY.length = rows; 275 for (myIt i = 0; i < cols; ++i)
316 int spacing = window.renderer.layoutSpacing; 276 for (myIt j = 0; j < rows; ++j)
317 277 {
318 int cum = 0; 278 IWidget widget = subWidgets[i + cols*j];
319 foreach (i, x; rowH) { 279 widget.setSize (col.width[i], row.width[j], wH, hH);
320 rowY[i] = cum; 280 widget.setPosition (x + col.pos[i], y + row.pos[j]);
321 cum += x + spacing; 281 }
322 }
323
324 cum = 0;
325 foreach (i, x; colW) {
326 colX[i] = cum;
327 cum += x + spacing;
328 }
329
330 // Tell subwidgets their new sizes:
331 foreach (i,widget; subWidgets)
332 widget.setSize (colW[i % cols], rowH[i / cols]);
333 } 282 }
334 //END Cache calculation functions 283 //END Cache calculation functions
335 284
336 /* Adjust the total size of rows/columns (including spacing) by diff.
337 *
338 * Params:
339 * cellD = current sizes; is adjusted by the function to new sizes
340 * cellDMin = minimal sizes (see colWMin/rowHMin)
341 * sizableCells= List of indexes in cellD for cells which are resizable
342 * diff = amount to increase/decrease the total size
343 * startHigh = if true, start resizing from the tail end of cellD, instead of the beginning
344 *
345 * Returns:
346 * The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.
347 */
348 int adjustCellSizes (ref int[] cellD, ref int[] cellDMin, ref size_t[] sizableCells, int diff, bool startHigh)
349 in {// Could occur if adjust isn't called first, but this would be a code error:
350 assert (cellD !is null, "adjustCellSizes: cellD is null");
351 } body {
352 // Cannot resize if no cells are sizable:
353 if (sizableCells.length == 0) return 0;
354
355 size_t si = (startHigh ? sizableCells.length-1 : 0); // starting index of sizableCells
356
357 // FIXME: could do with an overhaul
358 if (diff > 0) { // increase size of first resizable cell
359 cellD[sizableCells[si]] += diff;
360 return diff;
361 }
362 else if (diff < 0) { // decrease
363 size_t sc = (startHigh ? -1 : 1); // amount to iterate
364 size_t ci; // index in cellD
365 int rd = diff; // running diff
366 while (true) {
367 ci = sizableCells[si];
368
369 cellD[ci] += rd; // decrease this cell's size (but may be too much)
370 rd = cellD[ci] - cellDMin[ci];
371 if (rd >= 0) // OK; we're done
372 return diff;// we hit the mark exactly
373
374 // else we decreased it too much!
375 cellD[ci] = cellDMin[ci];
376 // rd is remainder to decrease by
377
378 si += sc; // iterate
379 if (si < 0 || si >= sizableCells.length) // run out of next cells
380 return diff - rd; // still had rd left to decrease
381 }
382 }
383 else // no adjustment needed
384 return 0;
385 }
386 285
387 //BEGIN Col/row resizing 286 //BEGIN Col/row resizing
388 void resizeCallback (ushort cx, ushort cy) { 287 void resizeCallback (ushort cx, ushort cy) {
389 int move; // NOTE: all resizing is relative, unlike in Window 288 col.resize (cx - dragX);
390 289 row.resize (cy - dragY);
391 if (resizeColsL.length != 0) { 290
392 move = cx - dragX; 291 // NOTE: all adjustments are relative; might be better if they were absolute?
393 292 dragX = cx;
394 // do shrinking first (in case we hit the minimum) 293 dragY = cy;
395 // Note: we rely on x[a..b] pointing to the same memory as x 294
396 if (move < 0) { 295 // NOTE: Resizing direction is set to "high direction" which isn't always going to be
397 move = -adjustCellSizes (colW, colWMin, resizeColsL, move, false); 296 // correct. A more accurate but more complex approach might be to get
398 adjustCellSizes (colW, colWMin, resizeColsH, move, true); 297 // adjustCellSizes to do the work.
399 } else { 298 setSubWidgetSP (true, true);
400 move = -adjustCellSizes (colW, colWMin, resizeColsH, -move, true);
401 adjustCellSizes (colW, colWMin, resizeColsL, move, false);
402 }
403
404 dragX = cx;
405 }
406
407 if (resizeRowsL.length != 0) {
408 move = cy - dragY;
409
410 // do shrinking first (in case we hit the minimum)
411 // Note: we rely on x[a..b] pointing to the same memory as x
412 if (move < 0) {
413 move = -adjustCellSizes (rowH, rowHMin, resizeRowsL, move, false);
414 adjustCellSizes (rowH, rowHMin, resizeRowsH, move, true);
415 } else {
416 move = -adjustCellSizes (rowH, rowHMin, resizeRowsH, -move, true);
417 adjustCellSizes (rowH, rowHMin, resizeRowsL, move, false);
418 }
419
420 dragY = cy;
421 }
422
423 // NOTE: this might be able to be made more efficient, but basically this all needs to happen:
424 genCachedMutableData;
425 setPosition (x,y);
426 window.requestRedraw; 299 window.requestRedraw;
427 } 300 }
428 bool endCallback (ushort cx, ushort cy, ubyte b, bool state) { 301 bool endCallback (ushort cx, ushort cy, ubyte b, bool state) {
429 if (b == 1 && state == false) { 302 if (b == 1 && state == false) {
430 window.gui.removeCallbacks (cast(void*) this); 303 window.gui.removeCallbacks (cast(void*) this);
431 // Remove unwanted data (although it shouldn't free any memory): 304 return true; // we've handled the up-click
432 resizeColsL = resizeColsH = resizeRowsL = resizeRowsH = null; 305 }
433 return true; // we've handled the up-click 306 return false; // we haven't handled it
434 }
435 return false; // we haven't handled it
436 } 307 }
437 308
438 protected: 309 protected:
439 // Data for resizing cols/rows: 310 // Data for resizing cols/rows:
440 int dragX, dragY; // coords where drag starts 311 int dragX, dragY; // coords where drag starts
441 // Lists of columns/rows with lower/higher index than the resize position
442 size_t[] resizeColsL, resizeColsH, resizeRowsL, resizeRowsH;
443 //END Col/row resizing 312 //END Col/row resizing
444 313
445 314
446 //BEGIN Construction (i.e. non-mutable) data 315 myIt cols, rows; // number of cells in grid
447 int cols, rows; // number of cells in grid
448 316
449 /* All widgets in the grid, by row. Order: [ 0 1 ] 317 /* All widgets in the grid, by row. Order: [ 0 1 ]
450 * [ 2 3 ] */ 318 * [ 2 3 ] */
451 IWidget[] subWidgets; 319 IWidget[] subWidgets;
452 320
453 // Cached data calculated from construction data: 321 /* Widths, positions, etc., either of columns or of rows
454 // Minimal column width / row height: 322 *
455 int[] colWMin, rowHMin; 323 * The purpose of this struct is mostly to unify functionality which must work the same on both
456 int mw, mh; // minimal dimensions 324 * horizontal and vertical cell placement.
457 325 *
458 // All resizable cols/rows, in order: 326 * Most notation corresponds to horizontal layout (columns), simply for easy of naming. */
459 size_t[] sizableCols, sizableRows; 327 struct CellDimensions {
460 //END Construction data 328 int[] pos, // relative position (cumulative width[i-1] plus spacing)
461 329 width, // current widths
462 330 minWidth; // minimal widths (set by genCachedConstructionData)
463 //BEGIN Mutable data 331 bool[] sizable; // true if col is resizable (set by genCachedConstructionData)
464 int[] colW, rowH; // column width / row height (largest size in col/row) 332 myDiff firstSizable, // first col which is resizable, negative if none
465 333 lastSizable; // as above, but last (set by genCachedConstructionData)
466 // Cached data calculated from construction and mutable data: 334 myDiff resizeD, // resize down from this index (<0 if not resizing)
467 int[] colX, rowY; // cumulative colW[i-1] + padding (add x to get column's left x-coord) 335 resizeU; // and up from this index
468 //END Mutable data 336 int spacing; // used by genPositions (which cannot access the layout class's data)
337
338 void dupMin () {
339 width = minWidth.dup;
340 }
341 void setCheck (int[] data) {
342 // Set to provided data:
343 width = data;
344 // And check sizes are valid:
345 foreach (i, m; minWidth)
346 // if fixed width or width is less than minimum:
347 if (!sizable[i] || width[i] < m)
348 width[i] = m;
349 }
350
351 // Generate position infomation and return total width (i.e. widget width/height)
352 int genPositions () {
353 pos.length = minWidth.length;
354
355 int x = 0;
356 foreach (i, w; width) {
357 pos[i] = x;
358 x += w + spacing;
359 }
360 return x - spacing;
361 }
362
363 // Get the row/column a click occured in
364 // Returns -i if in space to left of col i, or i if on col i
365 myDiff getCell (int l) {
366 myDiff i = minWidth.length - 1; // starting from right...
367 while (l < pos[i]) { // decrement while left of this column
368 debug assert (i > 0, "getCell: l < pos[0] (code error)");
369 --i;
370 } // now (l >= pos[i])
371 if (l >= pos[i] + width[i]) // between columns
372 return -i - 1; // note: i might be 0 so cannot just return -i
373 return i;
374 }
375
376 // Calculate resizeU/resizeD, and return true if unable to resize.
377 bool findResize (int l) {
378 resizeU = -getCell (l); // potential start for upward-resizes
379 if (resizeU <= 0) return true; // not on a space between cells
380 resizeD = resizeU - 1; // potential start for downward-resizes
381
382 while (!sizable[resizeU]) { // find first actually resizable column (upwards)
383 ++resizeU;
384 if (resizeU >= minWidth.length) { // cannot resize
385 resizeU = -1;
386 return true;
387 }
388 }
389
390 while (!sizable[resizeD]) { // find first actually resizable column (downwards)
391 --resizeD;
392 if (resizeD < 0) { // cannot resize
393 resizeU = -1; // resizeU is tested to check whether resizes are possible
394 return true;
395 }
396 }
397
398 return false; // can resize
399 }
400
401 /* Adjust the total size of rows/columns (including spacing) by diff.
402 *
403 * Params:
404 * diff = amount to increase/decrease the total size
405 * start= index for col/row to start resizing on
406 * incr = direction to resize in (added to index each step). Must be either -1 or +1.
407 *
408 * Returns:
409 * The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.
410 */
411 int adjustCellSizes (int diff, myDiff start, myDiff incr)
412 in {// Could occur if adjust isn't called first, but this would be a code error:
413 char[128] tmp;
414 logger.trace (logger.format (tmp, "start is {}", start));
415 assert (width !is null, "adjustCellSizes: width is null");
416 assert (start >= 0 && start < minWidth.length, "adjustCellSizes: invalid start");
417 assert (incr == 1 || incr == -1, "adjustCellSizes: invalid incr");
418 } body {
419 debug scope(failure)
420 logger.trace ("adjustCellSizes: failure");
421 myDiff i = start;
422 if (diff > 0) { // increase size of first resizable cell
423 width[i] += diff;
424 }
425 else if (diff < 0) { // decrease
426 int rd = diff; // running diff
427 aCSwhile:
428 while (true) {
429 width[i] += rd; // decrease this cell's size (but may be too much)
430 rd = width[i] - minWidth[i];
431 if (rd >= 0) // OK; we're done
432 break; // we hit the mark exactly: diff is correct
433
434 // else we decreased it too much!
435 width[i] = minWidth[i];
436 // rd is remainder to decrease by
437
438 bool it = true; // iterate (force first time)
439 while (it) {
440 i += incr;
441 if (i < 0 || i >= minWidth.length) { // run out of next cells
442 diff -= rd; // still had rd left to decrease
443 break aCSwhile; // exception: Array index out of bounds
444 }
445 it = !sizable[i]; // iterate again if row/col isn't resizable
446 }
447 }
448 }
449 // else no adjustment needed (diff == 0)
450
451 genPositions;
452 return diff;
453 }
454
455 void resize (int diff) {
456 if (resizeU <= 0) return;
457
458 // do shrinking first (in case we hit the minimum)
459 if (diff >= 0) {
460 diff = -adjustCellSizes (-diff, resizeU, 1);
461 adjustCellSizes (diff, resizeD, -1);
462 } else {
463 diff = -adjustCellSizes (diff, resizeD, -1);
464 adjustCellSizes (diff, resizeU, 1);
465 }
466 }
467 }
468 CellDimensions col, row;
469
470 // Index types. Note that in some cases they need to hold negative values.
471 alias size_t myIt;
472 alias ptrdiff_t myDiff;
469 } 473 }