comparison mde/gui/widget/layout.d @ 46:03fa79a48c48

Fixed resizing bugs in previous commit and made code cleaner and more efficient. setSize replaced by setWidth & setHeight. setPosition must be called after setWidth/Height. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 22 May 2008 12:51:47 +0100
parents 0fd51d2c6c8a
children d43523ed4b62
comparison
equal deleted inserted replaced
45:0fd51d2c6c8a 46:03fa79a48c48
35 * Since a grid with either dimension zero is not useful, there must be at least one sub-widget. 35 * Since a grid with either dimension zero is not useful, there must be at least one sub-widget.
36 * 36 *
37 * The grid has no border but has spacing between widgets. */ 37 * The grid has no border but has spacing between widgets. */
38 class GridLayoutWidget : Widget 38 class GridLayoutWidget : Widget
39 { 39 {
40 //BEGIN Creation & saving
40 this (IWindow wind, int[] data) { 41 this (IWindow wind, int[] data) {
41 // Get grid size and check data 42 // Get grid size and check data
42 // Check sufficient data for rows, cols, and at least one widget: 43 // Check sufficient data for rows, cols, and at least one widget:
43 if (data.length < 4) throw new WidgetDataException; 44 if (data.length < 4) throw new WidgetDataException;
44 super (wind, data); 45 super (wind, data);
60 61
61 // Calculate cached construction data 62 // Calculate cached construction data
62 genCachedConstructionData; 63 genCachedConstructionData;
63 } 64 }
64 65
66 /* This does two things:
67 * 1. Pass adjust data on to sub-widgets
68 * 2. Set the size, from the adjust data if possible
69 */
65 int[] adjust (int[] data) { 70 int[] adjust (int[] data) {
66 // Give all sub-widgets their data: 71 // Give all sub-widgets their data:
67 foreach (widget; subWidgets) 72 foreach (widget; subWidgets)
68 data = widget.adjust (data); 73 data = widget.adjust (data);
69 74
87 // Calculate column and row locations: 92 // Calculate column and row locations:
88 w = col.genPositions; 93 w = col.genPositions;
89 h = row.genPositions; 94 h = row.genPositions;
90 95
91 // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. 96 // Tell subwidgets their new sizes. Positions are given by a later call to setPosition.
92 foreach (i,widget; subWidgets) 97 foreach (i,widget; subWidgets) {
93 // Resizing direction is arbitrarily set to "high direction": 98 // Resizing direction is arbitrarily set to negative:
94 widget.setSize (col.width[i % cols], row.width[i / cols], true, true); 99 widget.setWidth (col.width[i % cols], -1);
100 widget.setHeight (row.width[i / cols], -1);
101 }
95 102
96 return data[lenUsed..$]; 103 return data[lenUsed..$];
97 } 104 }
98 105
99 int[] getCreationData () { 106 int[] getCreationData () {
113 ret ~= widget.getMutableData; 120 ret ~= widget.getMutableData;
114 121
115 ret ~= col.width ~ row.width; 122 ret ~= col.width ~ row.width;
116 return ret; 123 return ret;
117 } 124 }
118 125 //END Creation & saving
126
127 //BEGIN Size & position
119 bool isWSizable () { 128 bool isWSizable () {
120 return col.firstSizable >= 0; 129 return col.firstSizable >= 0;
121 } 130 }
122 bool isHSizable () { 131 bool isHSizable () {
123 return row.firstSizable >= 0; 132 return row.firstSizable >= 0;
127 void getMinimalSize (out int mw, out int mh) { 136 void getMinimalSize (out int mw, out int mh) {
128 mw = this.mw; 137 mw = this.mw;
129 mh = this.mh; 138 mh = this.mh;
130 } 139 }
131 140
132 void setSize (int nw, int nh, bool wHigh, bool hHigh) { 141 void setWidth (int nw, int dir) {
133 debug scope (failure) { 142 if (nw == w) return;
134 char[128] tmp; 143
135 logger.trace ("setSize failed: hHigh = " ~ (hHigh ? "true" : "false")); 144 w += col.adjustCellSizes (nw - w, (dir == -1 ? col.lastSizable : col.firstSizable), dir);
136 logger.trace (logger.format (tmp, "rows to resize: {}, {}", row.firstSizable, row.lastSizable)); 145
137 } 146 // Note: setPosition must be called after!
138 // Optimisation (could easily be called with same sizes if a parent layout widget is 147 }
139 // resized, since many columns/rows may not be resized). 148 void setHeight (int nh, int dir) {
140 if (nw == w && nh == h) return; 149 if (nh == h) return;
141 150
142 // calculate the row/column sizes (and new positions) 151 h += row.adjustCellSizes (nh - h, (dir == -1 ? row.lastSizable : row.firstSizable), dir);
143 if (wHigh) 152
144 w += col.adjustCellSizes (nw - w, col.lastSizable, -1); 153 // Note: setPosition must be called after!
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);
154 } 154 }
155 155
156 void setPosition (int x, int y) { 156 void setPosition (int x, int y) {
157 this.x = x; 157 this.x = x;
158 this.y = y; 158 this.y = y;
159 159
160 foreach (i,widget; subWidgets) 160 foreach (i,widget; subWidgets)
161 widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]); 161 widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]);
162 } 162 }
163 //END Size & position
163 164
164 165
165 // Find the relevant widget. 166 // Find the relevant widget.
166 IWidget getWidget (int cx, int cy) { 167 IWidget getWidget (int cx, int cy) {
167 debug scope (failure) 168 debug scope (failure)
208 //BEGIN Cache calculation functions 209 //BEGIN Cache calculation functions
209 /* Calculations which need to be run whenever a new sub-widget structure is set 210 /* Calculations which need to be run whenever a new sub-widget structure is set
210 * (i.e. to produce cached data calculated from construction data). */ 211 * (i.e. to produce cached data calculated from construction data). */
211 void genCachedConstructionData () { 212 void genCachedConstructionData () {
212 col.spacing = row.spacing = window.renderer.layoutSpacing; 213 col.spacing = row.spacing = window.renderer.layoutSpacing;
214 col.setColWidth = &setColWidth;
215 row.setColWidth = &setRowHeight;
213 216
214 // Calculate the minimal column and row sizes: 217 // Calculate the minimal column and row sizes:
215 // set length, making sure the arrays are initialised to zero: 218 // set length, making sure the arrays are initialised to zero:
216 col.minWidth = new int[cols]; 219 col.minWidth = new int[cols];
217 row.minWidth = new int[rows]; 220 row.minWidth = new int[rows];
267 row.sizable[row.lastSizable] = true; 270 row.sizable[row.lastSizable] = true;
268 if (row.firstSizable < 0) 271 if (row.firstSizable < 0)
269 row.firstSizable = row.lastSizable; 272 row.firstSizable = row.lastSizable;
270 } 273 }
271 } 274 }
272
273 // set sub-widgets size & position (done after resizing widget or rows/columns)
274 void setSubWidgetSP (bool wH, bool hH) {
275 for (myIt i = 0; i < cols; ++i)
276 for (myIt j = 0; j < rows; ++j)
277 {
278 IWidget widget = subWidgets[i + cols*j];
279 widget.setSize (col.width[i], row.width[j], wH, hH);
280 widget.setPosition (x + col.pos[i], y + row.pos[j]);
281 }
282 }
283 //END Cache calculation functions 275 //END Cache calculation functions
284 276
285 277
286 //BEGIN Col/row resizing 278 void setColWidth (myIt i, int w, int dir) {
279 for (myIt j = 0; j < rows; ++j) {
280 subWidgets[i + cols*j].setWidth (w, dir);
281 }
282 }
283 void setRowHeight (myIt j, int h, int dir) {
284 for (myIt i = 0; i < cols; ++i) {
285 subWidgets[i + cols*j].setHeight (h, dir);
286 }
287 }
288
289
290 //BEGIN Col/row resizing callback
287 void resizeCallback (ushort cx, ushort cy) { 291 void resizeCallback (ushort cx, ushort cy) {
288 col.resize (cx - dragX); 292 col.resize (cx - dragX);
289 row.resize (cy - dragY); 293 row.resize (cy - dragY);
290 294
291 // NOTE: all adjustments are relative; might be better if they were absolute? 295 // NOTE: all adjustments are relative; might be better if they were absolute?
292 dragX = cx; 296 dragX = cx;
293 dragY = cy; 297 dragY = cy;
294 298
295 // NOTE: Resizing direction is set to "high direction" which isn't always going to be 299 foreach (i,widget; subWidgets)
296 // correct. A more accurate but more complex approach might be to get 300 widget.setPosition (x + col.pos[i % cols],
297 // adjustCellSizes to do the work. 301 y + row.pos[i / cols]);
298 setSubWidgetSP (true, true);
299 window.requestRedraw; 302 window.requestRedraw;
300 } 303 }
301 bool endCallback (ushort cx, ushort cy, ubyte b, bool state) { 304 bool endCallback (ushort cx, ushort cy, ubyte b, bool state) {
302 if (b == 1 && state == false) { 305 if (b == 1 && state == false) {
303 window.gui.removeCallbacks (cast(void*) this); 306 window.gui.removeCallbacks (cast(void*) this);
307 } 310 }
308 311
309 protected: 312 protected:
310 // Data for resizing cols/rows: 313 // Data for resizing cols/rows:
311 int dragX, dragY; // coords where drag starts 314 int dragX, dragY; // coords where drag starts
312 //END Col/row resizing 315 //END Col/row resizing callback
313 316
314 317
315 myIt cols, rows; // number of cells in grid 318 myIt cols, rows; // number of cells in grid
316 319
317 /* All widgets in the grid, by row. Order: [ 0 1 ] 320 /* All widgets in the grid, by row. Order: [ 0 1 ]
332 myDiff firstSizable, // first col which is resizable, negative if none 335 myDiff firstSizable, // first col which is resizable, negative if none
333 lastSizable; // as above, but last (set by genCachedConstructionData) 336 lastSizable; // as above, but last (set by genCachedConstructionData)
334 myDiff resizeD, // resize down from this index (<0 if not resizing) 337 myDiff resizeD, // resize down from this index (<0 if not resizing)
335 resizeU; // and up from this index 338 resizeU; // and up from this index
336 int spacing; // used by genPositions (which cannot access the layout class's data) 339 int spacing; // used by genPositions (which cannot access the layout class's data)
340 /* This is a delegate to a enclosing class's function, since:
341 * a different implementation is needed for cols or rows
342 * we're unable to access enclosing class members directly */
343 void delegate (myIt,int,int) setColWidth; // set width of a column, with resize direction
337 344
338 void dupMin () { 345 void dupMin () {
339 width = minWidth.dup; 346 width = minWidth.dup;
340 } 347 }
341 void setCheck (int[] data) { 348 void setCheck (int[] data) {
405 * start= index for col/row to start resizing on 412 * 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. 413 * incr = direction to resize in (added to index each step). Must be either -1 or +1.
407 * 414 *
408 * Returns: 415 * Returns:
409 * The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin. 416 * The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.
417 *
418 * Note: Check variable used for start is valid before calling! If a non-sizable column's
419 * index is passed, this should get increased (if diff > 0) but not decreased.
410 */ 420 */
411 int adjustCellSizes (int diff, myDiff start, myDiff incr) 421 int adjustCellSizes (int diff, myDiff start, int incr)
412 in {// Could occur if adjust isn't called first, but this would be a code error: 422 in {
413 char[128] tmp; 423 // Could occur if adjust isn't called first, but this would be a code error:
414 logger.trace (logger.format (tmp, "start is {}", start));
415 assert (width !is null, "adjustCellSizes: width is null"); 424 assert (width !is null, "adjustCellSizes: width is null");
425 // Most likely if passed negative when sizing is disabled:
416 assert (start >= 0 && start < minWidth.length, "adjustCellSizes: invalid start"); 426 assert (start >= 0 && start < minWidth.length, "adjustCellSizes: invalid start");
417 assert (incr == 1 || incr == -1, "adjustCellSizes: invalid incr"); 427 assert (incr == 1 || incr == -1, "adjustCellSizes: invalid incr");
428 assert (setColWidth !is null, "adjustCellSizes: setColWidth is null");
418 } body { 429 } body {
419 debug scope(failure) 430 debug scope(failure)
420 logger.trace ("adjustCellSizes: failure"); 431 logger.trace ("adjustCellSizes: failure");
421 myDiff i = start; 432 myDiff i = start;
422 if (diff > 0) { // increase size of first resizable cell 433 if (diff > 0) { // increase size of first resizable cell
423 width[i] += diff; 434 width[i] += diff;
435 setColWidth (i, width[i], incr);
424 } 436 }
425 else if (diff < 0) { // decrease 437 else if (diff < 0) { // decrease
426 int rd = diff; // running diff 438 int rd = diff; // running diff
427 aCSwhile: 439 aCSwhile:
428 while (true) { 440 while (true) {
429 width[i] += rd; // decrease this cell's size (but may be too much) 441 width[i] += rd; // decrease this cell's size (but may be too much)
430 rd = width[i] - minWidth[i]; 442 rd = width[i] - minWidth[i];
431 if (rd >= 0) // OK; we're done 443 if (rd >= 0) { // OK; we're done
444 setColWidth (i, width[i], incr); // set new width
432 break; // we hit the mark exactly: diff is correct 445 break; // we hit the mark exactly: diff is correct
446 }
433 447
434 // else we decreased it too much! 448 // else we decreased it too much!
435 width[i] = minWidth[i]; 449 width[i] = minWidth[i];
450 setColWidth (i, width[i], incr);
436 // rd is remainder to decrease by 451 // rd is remainder to decrease by
452
437 453
438 bool it = true; // iterate (force first time) 454 bool it = true; // iterate (force first time)
439 while (it) { 455 while (it) {
440 i += incr; 456 i += incr;
441 if (i < 0 || i >= minWidth.length) { // run out of next cells 457 if (i < 0 || i >= minWidth.length) { // run out of next cells
466 } 482 }
467 } 483 }
468 CellDimensions col, row; 484 CellDimensions col, row;
469 485
470 // Index types. Note that in some cases they need to hold negative values. 486 // Index types. Note that in some cases they need to hold negative values.
487 // Int is used for resizing direction (although ptrdiff_t would be more appropriate),
488 // since the value must always be -1 or +1 and int is smaller on X86_64.
471 alias size_t myIt; 489 alias size_t myIt;
472 alias ptrdiff_t myDiff; 490 alias ptrdiff_t myDiff;
473 } 491 }