comparison mde/gui/widget/layout.d @ 111:1655693702fc

Resolved ticket #4, allowing widgets to reload strings and recalculate sizes mid-run. Removed prefinalize and finalize and added setup as the new second initialization phase, which can be re-run.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 06 Dec 2008 17:41:42 +0000
parents 2a1428ec5344
children fe061009029d
comparison
equal deleted inserted replaced
110:6acd96f8685f 111:1655693702fc
150 * Deriving classes should check data lengths, and set rows, cols, and the subWidgets array, 150 * Deriving classes should check data lengths, and set rows, cols, and the subWidgets array,
151 * before calling this super constructor. (If it's necessary to call super(...) first, 151 * before calling this super constructor. (If it's necessary to call super(...) first,
152 * the call to genCachedConstructionData can be moved to the derived this() methods.) 152 * the call to genCachedConstructionData can be moved to the derived this() methods.)
153 * 153 *
154 * Derived constructors may also set initWidths to the array of column widths followed by 154 * Derived constructors may also set initWidths to the array of column widths followed by
155 * row heights used to initially set the row/column dimensions. 155 * row heights used to initially set the row/column dimensions. */
156 *
157 * Sub-widgets are finalized here, so no methods should be called on sub-widgets before calling
158 * this super. */
159 protected this (IWidgetManager mgr, widgetID id, WidgetData data) { 156 protected this (IWidgetManager mgr, widgetID id, WidgetData data) {
160 super (mgr, id, data); 157 super (mgr, id, data);
161 158
162 // Create cell aligners with appropriate col/row adjustment function 159 // Create cell aligners with appropriate col/row adjustment function
163 if (data.ints[1] & 1) 160 if (data.ints[1] & 1)
164 col = AlignColumns.getInstance (id, cols); 161 col = AlignColumns.getInstance (id, cols);
165 else 162 else
166 col = (new AlignColumns (cols)); 163 col = (new AlignColumns (cols));
167 col.addSetCallback (&setColWidth); 164 col.addCallbacks (&setColWidth, &setupAlignDimData);
168 if (data.ints[1] & 2) 165 if (data.ints[1] & 2)
169 row = AlignColumns.getInstance (id~"R", rows); // id must be unique to that for cols! 166 row = AlignColumns.getInstance (id~"R", rows); // id must be unique to that for cols!
170 else 167 else
171 row = (new AlignColumns (rows)); 168 row = (new AlignColumns (rows));
172 row.addSetCallback (&setRowHeight); 169 row.addCallbacks (&setRowHeight, &setupAlignDimData);
173 useSpacing = (data.ints[1] & 4) != 0; 170 useSpacing = (data.ints[1] & 4) != 0;
174 } 171 }
175 172
176 /** Prior to finalizing but after sub-widgets are finalized, some information needs to be
177 * passed to the AlignColumns. */
178 void prefinalize () {
179 genCachedConstructionData; // min widths, sizableness
180 }
181
182 /** Responsible for calculating the minimal size and initializing some stuff. 173 /** Responsible for calculating the minimal size and initializing some stuff.
183 * 174 *
184 * As such, this must be the first function called after this(). */ 175 * As such, this must be the first function called after this(). */
185 void finalize () { 176 bool setup (uint n, uint flags) {
186 if (initWidths.length == cols + rows) { 177 // Run all internal calculations regardless of changes, then check dimensions for changes.
187 col.setWidths (initWidths[0..cols]); 178 // Don't try shortcutting internal calculations when there are no changes - I've tried, and
188 row.setWidths (initWidths[cols..$]); 179 // doing so adds enough overhead to make doing so almost(?) worthless (or at least large
189 } else { 180 // increases in complexity).
190 col.setWidths; 181 wdim ow = w, oh = h;
191 row.setWidths; 182
192 } 183 col.setup (n, flags);
193 initWidths = null; // free 184 row.setup (n, flags);
194 185
195 mw = col.mw; 186 if (initWidths.length == cols + rows) {
196 mh = row.mw; 187 col.setWidths (initWidths[0..cols]);
197 w = col.w; 188 row.setWidths (initWidths[cols..$]);
198 h = row.w; 189 } else {
199 190 col.setWidths;
200 // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. 191 row.setWidths;
201 foreach (i,widget; subWidgets) { 192 }
202 // Resizing direction is arbitrarily set to negative: 193 initWidths = null; // free
203 widget.setWidth (col.width[i % cols], -1); 194
204 widget.setHeight (row.width[i / cols], -1); 195 mw = col.mw;
205 } 196 mh = row.mw;
197 w = col.w;
198 h = row.w;
199
200 // Tell subwidgets their new sizes. Positions are given by a later call to setPosition.
201 foreach (i,widget; subWidgets) {
202 // Resizing direction is arbitrarily set to negative:
203 widget.setWidth (col.width[i % cols], -1);
204 widget.setHeight (row.width[i / cols], -1);
205 }
206 return (ow != w || oh != h);
206 } 207 }
207 //END Creation & saving 208 //END Creation & saving
208 209
209 //BEGIN Size & position 210 //BEGIN Size & position
210 bool isWSizable () { 211 bool isWSizable () {
282 mgr.renderer.drawSpacers (x,y, w,h, col.pos[1..$], row.pos[1..$]); 283 mgr.renderer.drawSpacers (x,y, w,h, col.pos[1..$], row.pos[1..$]);
283 } 284 }
284 285
285 package: 286 package:
286 /* Calculations which need to be run whenever a new sub-widget structure is set 287 /* Calculations which need to be run whenever a new sub-widget structure is set
287 * (i.e. to produce cached data calculated from construction data). 288 * or other changes affecting widget sizes. Most of these need to happen regardless of whether
288 * Also need to be re-run if the renderer changes. 289 * changes have occurred, since AlignColumns have been reset.
289 * 290 *
290 * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns 291 * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns
291 * (col and row). subWidgets need to know their minimal size and resizability. */ 292 * (col and row). subWidgets need to know their minimal size and resizability. */
292 void genCachedConstructionData () { 293 void setupAlignDimData (uint n, uint flags) {
293 // Will only change if renderer changes: 294 if (sADD_n == n) return; // cached data is current
294 // NOTE shared AlignColumns get this set by all sharing GridWidgets 295 sADD_n = n;
296
297 foreach (widg; subWidgets) // make sure all subwidgets have been set up
298 widg.setup (n,flags);
299 // make sure both AlignColumns are set up (since first call to setup(n) calls reset):
300 col.setup (n, flags);
301 row.setup (n, flags);
302
303 // Note: shared AlignColumns get this set by all sharing GridWidgets
295 col.spacing = row.spacing = useSpacing ? mgr.renderer.layoutSpacing : 0; 304 col.spacing = row.spacing = useSpacing ? mgr.renderer.layoutSpacing : 0;
296 305
297 // Calculate the minimal column and row sizes: 306 // Calculate the minimal column and row sizes:
298 // AlignColumns (row, col) takes care of initializing minWidth. 307 // AlignColumns (row, col) takes care of initializing minWidth.
299 foreach (i,widget; subWidgets) { 308 foreach (i,widget; subWidgets) {
300 // Increase dimensions if current minimal size is larger: 309 // Increase dimensions if current minimal size is larger:
301 myIt n = i % cols; // column 310 myIt j = i % cols; // column
302 wdim md = widget.minWidth; 311 wdim md = widget.minWidth;
303 if (col.minWidth[n] < md) col.minWidth[n] = md; 312 if (col.minWidth[j] < md) col.minWidth[j] = md;
304 n = i / cols; // row 313 j = i / cols; // row
305 md = widget.minHeight; 314 md = widget.minHeight;
306 if (row.minWidth[n] < md) row.minWidth[n] = md; 315 if (row.minWidth[j] < md) row.minWidth[j] = md;
307 } 316 }
308 317
309 // Find which cols/rows are resizable: 318 // Find which cols/rows are resizable:
310 // AlignColumns initializes sizable, and sets first and last sizables. 319 // AlignColumns initializes sizable, and sets first and last sizables.
311 forCols: 320 forCols:
368 wdim dragX, dragY; // coords where drag starts 377 wdim dragX, dragY; // coords where drag starts
369 //END Col/row resizing callback 378 //END Col/row resizing callback
370 379
371 myIt cols, rows; // number of cells in grid 380 myIt cols, rows; // number of cells in grid
372 wdim[] initWidths; // see this / setInitialSize 381 wdim[] initWidths; // see this / setInitialSize
373 bool useSpacing; // true if spacing should be applied 382 uint sADD_n = uint.max; // param n of last setup call after setupAlignDimData has run
383 bool useSpacing; // add inter-row/col spacing?
374 384
375 /* All widgets in the grid, by row. Order: [ 0 1 ] 385 /* All widgets in the grid, by row. Order: [ 0 1 ]
376 * [ 2 3 ] */ 386 * [ 2 3 ] */
377 //IChildWidget[] subWidgets; 387 //IChildWidget[] subWidgets;
378 388
415 * reset. 425 * reset.
416 * 426 *
417 * After creation, minimal widths should be set for all columns (minWidth) and 427 * After creation, minimal widths should be set for all columns (minWidth) and
418 * setWidths must be called before other functions are used. */ 428 * setWidths must be called before other functions are used. */
419 this (myIt columns) { 429 this (myIt columns) {
420 reset (columns); 430 if (columns < 1)
431 throw new GuiException("AlignColumns: created with <1 column (code error)");
432 minWidth.length = columns;
433 sizable.length = columns;
434 }
435
436 /** Like IChildWidget's setup; calls sADD delegates. */
437 void setup (uint n, uint flags) {
438 if (n != setup_n) {
439 logger.trace ("AlignColumns.setup ({}): {}", n, cast(void*)this);
440 setup_n = n;
441 setupWidths = false;
442 reset (minWidth.length);
443
444 foreach (dg; sADD)
445 dg (n, flags); // set flag 1
446 }
421 } 447 }
422 448
423 /** Reset all column information (only keep set callbacks). 449 /** Reset all column information (only keep set callbacks).
424 * 450 *
425 * Widths should be set after calling, as on creation. */ 451 * Widths should be set after calling, as on creation. */
426 void reset (myIt columns) { 452 void reset (myIt columns) {
427 if (columns < 1) 453 minWidth[] = 0;
428 throw new GuiException("AlignColumns: created with <1 column (code error)"); 454 sizable[] = false;
429 minWidth = new wdim[columns];
430 sizable = new bool[columns];
431 width = null; // enforce calling setWidths after this
432 firstSizable = -1; 455 firstSizable = -1;
433 lastSizable = -1; 456 lastSizable = -1;
434 } 457 }
435 458
436 /** Initialize widths, either from minWidths or from supplied list, checking validity. 459 /** Initialize widths, either from minWidths or from supplied list, checking validity.
437 * 460 *
438 * Also calculates first/lastSizable from sizable, overall minimal width and column positions. 461 * Also calculates first/lastSizable from sizable, overall minimal width and column positions.
439 */ 462 */
440 void setWidths (wdim[] data = null) { 463 void setWidths (wdim[] data = null) {
441 if (!width) { 464 if (!setupWidths) {
442 if (data) { 465 logger.trace ("setWidths");
443 debug assert (data.length == minWidth.length, "setWidths called with bad data length (code error)"); 466 setupWidths = true;
444 width = data.dup; // data is shared by other widgets with same id so must be .dup'ed 467 if (data || width) { // use existing/external data: need to check validity
445 // And check sizes are valid: 468 if (data) {
469 assert (data.length == minWidth.length, "setWidths called with bad data length (code error)");
470 width = data.dup; // data is shared by other widgets with same id so must be .dup'ed
471 }
446 foreach (i, m; minWidth) { 472 foreach (i, m; minWidth) {
447 if (!sizable[i] || width[i] < m) // if width is fixed or less than minimum 473 if (!sizable[i] || width[i] < m) // if width is fixed or less than minimum
448 width[i] = m; 474 width[i] = m;
449 } 475 }
450 } else 476 } else
472 } 498 }
473 } 499 }
474 } 500 }
475 } 501 }
476 502
477 /** Add a callback to be called to notify changes in a column's width. 503 /** Add a callback to be called to notify changes in a column's width, and the sADD callback.
478 * 504 */
479 * All callbacks added are called on a width change so that multiple objects may share a 505 typeof(this) addCallbacks (void delegate (myIt,wdim,int) setCW, void delegate (uint,uint) sDg) {
480 * CellAlign object. */ 506 assert (setCW && sDg, "AlignColumns.addCallbacks: null callback (code error)");
481 typeof(this) addSetCallback (void delegate (myIt,wdim,int) setCW) {
482 assert (setCW, "CellAlign.this: setCW is null (code error)");
483 setWidthCb ~= setCW; 507 setWidthCb ~= setCW;
508 sADD ~= sDg;
484 return this; 509 return this;
485 } 510 }
486 511
487 /** Get the row/column of relative position l. 512 /** Get the row/column of relative position l.
488 * 513 *
667 wdim w,mw; // current & minimal widths 692 wdim w,mw; // current & minimal widths
668 /* indicies of the first/last resizable column (negative if none are resizable). */ 693 /* indicies of the first/last resizable column (negative if none are resizable). */
669 myDiff firstSizable = -1, lastSizable = -1; // set by calcFLSbl 694 myDiff firstSizable = -1, lastSizable = -1; // set by calcFLSbl
670 // Callbacks used to actually adjust a column's width: 695 // Callbacks used to actually adjust a column's width:
671 void delegate (myIt,wdim,int) setWidthCb[]; // set width of a column, with resize direction 696 void delegate (myIt,wdim,int) setWidthCb[]; // set width of a column, with resize direction
697 void delegate (uint,uint) sADD[]; // setupAlignDimData dlgs
698
699 uint setup_n = uint.max; // param n of last setup call
700 bool setupWidths; // setWidths has been run
672 701
673 static HashMap!(widgetID,AlignColumns) instances; 702 static HashMap!(widgetID,AlignColumns) instances;
674 static this () { 703 static this () {
675 instances = new HashMap!(widgetID,AlignColumns); 704 instances = new HashMap!(widgetID,AlignColumns);
676 } 705 }