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