diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-30 15:03:04 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-30 15:03:04 +0300 |
commit | 8ab2c6c2a27f888602ea05afaf05a75ecca9bf02 (patch) | |
tree | f6160a88fa05ae2fc6b34c04d96f5cc637f40ba4 /src/ui/widget.c | |
parent | 23f70040368e8d5577f89b2bacf303b5dac8561f (diff) |
Widget: Arrangement debugging; min size
There is a problem with repeated arrangements: the previous set sizes affect subsequent outcomes. This results in Preferences not being able to reduce in size, only to expand.
It should be possible to reset sizes back to zero/minimum size before starting an arrangement, but LabelWidget needs to cooperate by using `minSize` to set its default size. `minSize` is preferable to fixedSize because then the widget can go through the usual arranging logic.
To be continued at a later time...
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r-- | src/ui/widget.c | 222 |
1 files changed, 176 insertions, 46 deletions
diff --git a/src/ui/widget.c b/src/ui/widget.c index 91f6c12d..baabf673 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -86,6 +86,7 @@ void init_Widget(iWidget *d) { | |||
86 | init_String(&d->id); | 86 | init_String(&d->id); |
87 | d->flags = 0; | 87 | d->flags = 0; |
88 | d->rect = zero_Rect(); | 88 | d->rect = zero_Rect(); |
89 | d->minSize = zero_I2(); | ||
89 | d->bgColor = none_ColorId; | 90 | d->bgColor = none_ColorId; |
90 | d->frameColor = none_ColorId; | 91 | d->frameColor = none_ColorId; |
91 | init_Anim(&d->visualOffset, 0.0f); | 92 | init_Anim(&d->visualOffset, 0.0f); |
@@ -188,20 +189,25 @@ void setPos_Widget(iWidget *d, iInt2 pos) { | |||
188 | setFlags_Widget(d, fixedPosition_WidgetFlag, iTrue); | 189 | setFlags_Widget(d, fixedPosition_WidgetFlag, iTrue); |
189 | } | 190 | } |
190 | 191 | ||
191 | void setSize_Widget(iWidget *d, iInt2 size) { | 192 | void setFixedSize_Widget(iWidget *d, iInt2 fixedSize) { |
192 | int flags = fixedSize_WidgetFlag; | 193 | int flags = fixedSize_WidgetFlag; |
193 | if (size.x < 0) { | 194 | if (fixedSize.x < 0) { |
194 | size.x = d->rect.size.x; | 195 | fixedSize.x = d->rect.size.x; |
195 | flags &= ~fixedWidth_WidgetFlag; | 196 | flags &= ~fixedWidth_WidgetFlag; |
196 | } | 197 | } |
197 | if (size.y < 0) { | 198 | if (fixedSize.y < 0) { |
198 | size.y = d->rect.size.y; | 199 | fixedSize.y = d->rect.size.y; |
199 | flags &= ~fixedHeight_WidgetFlag; | 200 | flags &= ~fixedHeight_WidgetFlag; |
200 | } | 201 | } |
201 | d->rect.size = size; | 202 | d->rect.size = fixedSize; |
202 | setFlags_Widget(d, flags, iTrue); | 203 | setFlags_Widget(d, flags, iTrue); |
203 | } | 204 | } |
204 | 205 | ||
206 | void setMinSize_Widget(iWidget *d, iInt2 minSize) { | ||
207 | d->minSize = minSize; | ||
208 | /* rearranging needed to apply this */ | ||
209 | } | ||
210 | |||
205 | void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { | 211 | void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { |
206 | d->padding[0] = left; | 212 | d->padding[0] = left; |
207 | d->padding[1] = top; | 213 | d->padding[1] = top; |
@@ -268,33 +274,79 @@ static int widestChild_Widget_(const iWidget *d) { | |||
268 | return width; | 274 | return width; |
269 | } | 275 | } |
270 | 276 | ||
277 | static void arrange_Widget_(iWidget *); | ||
278 | static const iBool tracing_ = iFalse; | ||
279 | |||
280 | #define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); } | ||
281 | |||
282 | static int depth_Widget_(const iWidget *d) { | ||
283 | int depth = 0; | ||
284 | for (const iWidget *w = d->parent; w; w = w->parent) { | ||
285 | depth++; | ||
286 | } | ||
287 | return depth; | ||
288 | } | ||
289 | |||
290 | static void printf_Widget_(const iWidget *d, const char *format, ...) { | ||
291 | va_list args; | ||
292 | va_start(args, format); | ||
293 | iString *msg = new_String(); | ||
294 | for (size_t i = 0; i < depth_Widget_(d); ++i) { | ||
295 | appendCStr_String(msg, "| "); | ||
296 | } | ||
297 | appendFormat_String(msg, "[%p] %s(%s) ", d, class_Widget(d)->name, cstr_String(id_Widget(d))); | ||
298 | while (size_String(msg) < 44 + depth_Widget_(d) * 4) { | ||
299 | appendCStr_String(msg, " "); | ||
300 | } | ||
301 | iBlock *msg2 = new_Block(0); | ||
302 | vprintf_Block(msg2, format, args); | ||
303 | va_end(args); | ||
304 | printf("%s%s\n", cstr_String(msg), cstr_Block(msg2)); | ||
305 | delete_Block(msg2); | ||
306 | delete_String(msg); | ||
307 | } | ||
308 | |||
271 | static void setWidth_Widget_(iWidget *d, int width) { | 309 | static void setWidth_Widget_(iWidget *d, int width) { |
272 | iAssert(width >= 0); | 310 | iAssert(width >= 0); |
311 | TRACE(d, "attempt to set width to %d (current: %d, min width: %d)", width, d->rect.size.x, d->minSize.x); | ||
312 | width = iMax(width, d->minSize.x); | ||
273 | if (~d->flags & fixedWidth_WidgetFlag || d->flags & collapse_WidgetFlag) { | 313 | if (~d->flags & fixedWidth_WidgetFlag || d->flags & collapse_WidgetFlag) { |
274 | if (d->rect.size.x != width) { | 314 | if (d->rect.size.x != width) { |
275 | d->rect.size.x = width; | 315 | d->rect.size.x = width; |
316 | TRACE(d, "width has changed to %d", width); | ||
276 | if (class_Widget(d)->sizeChanged) { | 317 | if (class_Widget(d)->sizeChanged) { |
277 | const int oldHeight = d->rect.size.y; | 318 | const int oldHeight = d->rect.size.y; |
278 | class_Widget(d)->sizeChanged(d); | 319 | class_Widget(d)->sizeChanged(d); |
279 | if (d->rect.size.y != oldHeight) { | 320 | if (d->rect.size.y != oldHeight) { |
321 | TRACE(d, "sizeChanged() cuased height change to %d; redoing parent", d->rect.size.y); | ||
280 | /* Widget updated its height. */ | 322 | /* Widget updated its height. */ |
281 | arrange_Widget(d->parent); | 323 | arrange_Widget_(d->parent); |
324 | TRACE(d, "parent layout redone"); | ||
282 | } | 325 | } |
283 | } | 326 | } |
284 | } | 327 | } |
285 | } | 328 | } |
329 | else { | ||
330 | TRACE(d, "changing width not allowed; flags: %x", d->flags); | ||
331 | } | ||
286 | } | 332 | } |
287 | 333 | ||
288 | static void setHeight_Widget_(iWidget *d, int height) { | 334 | static void setHeight_Widget_(iWidget *d, int height) { |
289 | iAssert(height >= 0); | 335 | iAssert(height >= 0); |
336 | TRACE(d, "attempt to set height to %d (current: %d, min height: %d)", height, d->rect.size.y, d->minSize.y); | ||
337 | height = iMax(height, d->minSize.y); | ||
290 | if (~d->flags & fixedHeight_WidgetFlag || d->flags & collapse_WidgetFlag) { | 338 | if (~d->flags & fixedHeight_WidgetFlag || d->flags & collapse_WidgetFlag) { |
291 | if (d->rect.size.y != height) { | 339 | if (d->rect.size.y != height) { |
292 | d->rect.size.y = height; | 340 | d->rect.size.y = height; |
341 | TRACE(d, "height has changed to %d", height); | ||
293 | if (class_Widget(d)->sizeChanged) { | 342 | if (class_Widget(d)->sizeChanged) { |
294 | class_Widget(d)->sizeChanged(d); | 343 | class_Widget(d)->sizeChanged(d); |
295 | } | 344 | } |
296 | } | 345 | } |
297 | } | 346 | } |
347 | else { | ||
348 | TRACE(d, "changing height not allowed; flags: %x", d->flags); | ||
349 | } | ||
298 | } | 350 | } |
299 | 351 | ||
300 | iLocalDef iBool isCollapsed_Widget_(const iWidget *d) { | 352 | iLocalDef iBool isCollapsed_Widget_(const iWidget *d) { |
@@ -336,29 +388,63 @@ static void centerHorizontal_Widget_(iWidget *d) { | |||
336 | : rootSize_Window(get_Window()).x) - | 388 | : rootSize_Window(get_Window()).x) - |
337 | width_Rect(d->rect)) / | 389 | width_Rect(d->rect)) / |
338 | 2; | 390 | 2; |
391 | TRACE(d, "center horizontally: %d", d->rect.pos.x); | ||
339 | } | 392 | } |
340 | 393 | ||
341 | void arrange_Widget(iWidget *d) { | 394 | static void boundsOfChildren_Widget_(const iWidget *d, iRect *bounds_out) { |
395 | *bounds_out = zero_Rect(); | ||
396 | iConstForEach(ObjectList, i, d->children) { | ||
397 | const iWidget *child = constAs_Widget(i.object); | ||
398 | if (isCollapsed_Widget_(child)) { | ||
399 | continue; | ||
400 | } | ||
401 | iRect childRect = child->rect; | ||
402 | if (child->flags & ignoreForParentWidth_WidgetFlag) { | ||
403 | childRect.size.x = 0; | ||
404 | } | ||
405 | if (isEmpty_Rect(*bounds_out)) { | ||
406 | *bounds_out = childRect; | ||
407 | } | ||
408 | else { | ||
409 | *bounds_out = union_Rect(*bounds_out, childRect); | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | |||
414 | static void arrange_Widget_(iWidget *d) { | ||
415 | TRACE(d, "arranging..."); | ||
342 | if (isCollapsed_Widget_(d)) { | 416 | if (isCollapsed_Widget_(d)) { |
417 | TRACE(d, "collapsed => END"); | ||
343 | setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); | 418 | setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); |
344 | return; | 419 | return; |
345 | } | 420 | } |
346 | if (d->flags & moveToParentLeftEdge_WidgetFlag) { | 421 | if (d->flags & moveToParentLeftEdge_WidgetFlag) { |
347 | d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */ | 422 | d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */ |
423 | TRACE(d, "move to parent left edge: %d", d->rect.pos.x); | ||
348 | } | 424 | } |
349 | else if (d->flags & moveToParentRightEdge_WidgetFlag) { | 425 | else if (d->flags & moveToParentRightEdge_WidgetFlag) { |
350 | d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); | 426 | d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); |
427 | TRACE(d, "move to parent right edge: %d", d->rect.pos.x); | ||
351 | } | 428 | } |
352 | else if (d->flags & moveToParentBottomEdge_WidgetFlag) { | 429 | else if (d->flags & moveToParentBottomEdge_WidgetFlag) { |
353 | d->rect.pos.y = height_Rect(innerRect_Widget_(d->parent)) - height_Rect(d->rect); | 430 | d->rect.pos.y = height_Rect(innerRect_Widget_(d->parent)) - height_Rect(d->rect); |
431 | TRACE(d, "move to parent bottom edge: %d", d->rect.pos.y); | ||
354 | } | 432 | } |
355 | else if (d->flags & centerHorizontal_WidgetFlag) { | 433 | else if (d->flags & centerHorizontal_WidgetFlag) { |
356 | centerHorizontal_Widget_(d); | 434 | centerHorizontal_Widget_(d); |
357 | } | 435 | } |
358 | if (d->flags & resizeToParentWidth_WidgetFlag) { | 436 | if (d->flags & resizeToParentWidth_WidgetFlag) { |
359 | setWidth_Widget_(d, width_Rect(innerRect_Widget_(d->parent))); | 437 | iRect childBounds = zero_Rect(); |
438 | if (flags_Widget(d->parent) & arrangeWidth_WidgetFlag) { | ||
439 | /* Can't go narrower than what the children require, though. */ | ||
440 | boundsOfChildren_Widget_(d, &childBounds); | ||
441 | } | ||
442 | TRACE(d, "resize to parent width; child bounds width %d", childBounds.size.x, childBounds.size.y); | ||
443 | setWidth_Widget_(d, iMaxi(width_Rect(innerRect_Widget_(d->parent)), | ||
444 | width_Rect(childBounds))); | ||
360 | } | 445 | } |
361 | if (d->flags & resizeToParentHeight_WidgetFlag) { | 446 | if (d->flags & resizeToParentHeight_WidgetFlag) { |
447 | TRACE(d, "resize to parent height"); | ||
362 | setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent))); | 448 | setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent))); |
363 | } | 449 | } |
364 | if (d->flags & safePadding_WidgetFlag) { | 450 | if (d->flags & safePadding_WidgetFlag) { |
@@ -370,46 +456,44 @@ void arrange_Widget(iWidget *d) { | |||
370 | } | 456 | } |
371 | /* The rest of the arrangement depends on child widgets. */ | 457 | /* The rest of the arrangement depends on child widgets. */ |
372 | if (!d->children) { | 458 | if (!d->children) { |
459 | TRACE(d, "no children => END"); | ||
373 | return; | 460 | return; |
374 | } | 461 | } |
375 | const size_t childCount = numArrangedChildren_Widget_(d); | 462 | const size_t childCount = numArrangedChildren_Widget_(d); |
376 | /* There may still be unarranged children that need arranging internally. */ | 463 | TRACE(d, "%d arranged children", childCount); |
377 | if (childCount == 0) { | ||
378 | iForEach(ObjectList, i, d->children) { | ||
379 | iWidget *child = as_Widget(i.object); | ||
380 | if (isArranged_Widget_(child)) { | ||
381 | arrange_Widget(child); | ||
382 | } | ||
383 | } | ||
384 | return; | ||
385 | } | ||
386 | /* Resize children to fill the parent widget. */ | 464 | /* Resize children to fill the parent widget. */ |
387 | if (d->flags & resizeChildren_WidgetFlag) { | 465 | if (d->flags & resizeChildren_WidgetFlag) { |
388 | const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0, | 466 | const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0, |
389 | (d->flags & resizeHeightOfChildren_WidgetFlag) != 0); | 467 | (d->flags & resizeHeightOfChildren_WidgetFlag) != 0); |
468 | TRACE(d, "resize children, x:%d y:%d", dirs.x, dirs.y); | ||
390 | /* Collapse hidden children. */ | 469 | /* Collapse hidden children. */ |
391 | iBool collapseChanged = iFalse; | 470 | iBool collapseChanged = iFalse; |
392 | iForEach(ObjectList, c, d->children) { | 471 | iForEach(ObjectList, c, d->children) { |
393 | iWidget *child = as_Widget(c.object); | 472 | iWidget *child = as_Widget(c.object); |
394 | if (!isCollapsed_Widget_(child) && child->flags & wasCollapsed_WidgetFlag) { | 473 | if (!isCollapsed_Widget_(child) && child->flags & wasCollapsed_WidgetFlag) { |
395 | setFlags_Widget(child, wasCollapsed_WidgetFlag, iFalse); | 474 | setFlags_Widget(child, wasCollapsed_WidgetFlag, iFalse); |
475 | TRACE(d, "child %p is uncollapsed", child); | ||
396 | /* Undo collapse and determine the normal size again. */ | 476 | /* Undo collapse and determine the normal size again. */ |
397 | arrange_Widget(d); | 477 | arrange_Widget_(d); |
398 | collapseChanged = iTrue; | 478 | collapseChanged = iTrue; |
399 | } | 479 | } |
400 | else if (isCollapsed_Widget_(child) && ~child->flags & wasCollapsed_WidgetFlag) { | 480 | else if (isCollapsed_Widget_(child) && ~child->flags & wasCollapsed_WidgetFlag) { |
401 | setFlags_Widget(child, wasCollapsed_WidgetFlag, iTrue); | 481 | setFlags_Widget(child, wasCollapsed_WidgetFlag, iTrue); |
402 | collapseChanged = iTrue; | 482 | collapseChanged = iTrue; |
483 | TRACE(d, "child %p flagged as collapsed", child); | ||
403 | } | 484 | } |
404 | } | 485 | } |
405 | if (collapseChanged) { | 486 | if (collapseChanged) { |
406 | arrange_Widget(d); /* Redo with the new child sizes. */ | 487 | TRACE(d, "redoing arrangement due to changes in child collapse state"); |
488 | arrange_Widget_(d); /* Redo with the new child sizes. */ | ||
407 | return; | 489 | return; |
408 | } | 490 | } |
409 | const int expCount = numExpandingChildren_Widget_(d); | 491 | const int expCount = numExpandingChildren_Widget_(d); |
492 | TRACE(d, "%d expanding children", expCount); | ||
410 | /* Only resize the expanding children, not touching the others. */ | 493 | /* Only resize the expanding children, not touching the others. */ |
411 | if (expCount > 0) { | 494 | if (expCount > 0) { |
412 | iInt2 avail = innerRect_Widget_(d).size; | 495 | iInt2 avail = innerRect_Widget_(d).size; |
496 | TRACE(d, "inner size: %dx%d", avail.x, avail.y); | ||
413 | iConstForEach(ObjectList, i, d->children) { | 497 | iConstForEach(ObjectList, i, d->children) { |
414 | const iWidget *child = constAs_Widget(i.object); | 498 | const iWidget *child = constAs_Widget(i.object); |
415 | if (!isArranged_Widget_(child)) { | 499 | if (!isArranged_Widget_(child)) { |
@@ -420,9 +504,11 @@ void arrange_Widget(iWidget *d) { | |||
420 | } | 504 | } |
421 | } | 505 | } |
422 | avail = divi_I2(max_I2(zero_I2(), avail), expCount); | 506 | avail = divi_I2(max_I2(zero_I2(), avail), expCount); |
507 | TRACE(d, "changing child sizes (expand mode)..."); | ||
423 | iForEach(ObjectList, j, d->children) { | 508 | iForEach(ObjectList, j, d->children) { |
424 | iWidget *child = as_Widget(j.object); | 509 | iWidget *child = as_Widget(j.object); |
425 | if (!isArranged_Widget_(child)) { | 510 | if (!isArranged_Widget_(child)) { |
511 | TRACE(d, "child %p is not arranged", child); | ||
426 | continue; | 512 | continue; |
427 | } | 513 | } |
428 | if (child->flags & expand_WidgetFlag) { | 514 | if (child->flags & expand_WidgetFlag) { |
@@ -445,6 +531,7 @@ void arrange_Widget(iWidget *d) { | |||
445 | } | 531 | } |
446 | } | 532 | } |
447 | } | 533 | } |
534 | TRACE(d, "...done changing child sizes (expand mode)"); | ||
448 | } | 535 | } |
449 | else { | 536 | else { |
450 | /* Evenly size all children. */ | 537 | /* Evenly size all children. */ |
@@ -458,6 +545,7 @@ void arrange_Widget(iWidget *d) { | |||
458 | childSize.y /= childCount; | 545 | childSize.y /= childCount; |
459 | unpaddedChildSize.y /= childCount; | 546 | unpaddedChildSize.y /= childCount; |
460 | } | 547 | } |
548 | TRACE(d, "begin changing child sizes (EVEN mode)..."); | ||
461 | iForEach(ObjectList, i, d->children) { | 549 | iForEach(ObjectList, i, d->children) { |
462 | iWidget *child = as_Widget(i.object); | 550 | iWidget *child = as_Widget(i.object); |
463 | if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { | 551 | if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { |
@@ -468,34 +556,52 @@ void arrange_Widget(iWidget *d) { | |||
468 | setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y); | 556 | setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y); |
469 | } | 557 | } |
470 | } | 558 | } |
559 | else { | ||
560 | TRACE(d, "child %p cannot be resized (parentCannotResize: %d)", child, | ||
561 | (child->flags & parentCannotResize_WidgetFlag) != 0); | ||
562 | } | ||
471 | } | 563 | } |
564 | TRACE(d, "...done changing child sizes (EVEN mode)"); | ||
472 | } | 565 | } |
473 | } | 566 | } |
474 | if (d->flags & resizeChildrenToWidestChild_WidgetFlag) { | 567 | if (d->flags & resizeChildrenToWidestChild_WidgetFlag) { |
475 | const int widest = widestChild_Widget_(d); | 568 | const int widest = widestChild_Widget_(d); |
569 | TRACE(d, "resizing children to widest child (%d)...", widest); | ||
476 | iForEach(ObjectList, i, d->children) { | 570 | iForEach(ObjectList, i, d->children) { |
477 | iWidget *child = as_Widget(i.object); | 571 | iWidget *child = as_Widget(i.object); |
478 | if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { | 572 | if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { |
479 | setWidth_Widget_(child, widest); | 573 | setWidth_Widget_(child, widest); |
480 | } | 574 | } |
575 | else { | ||
576 | TRACE(d, "child %p cannot be resized (parentCannotResize: %d)", child, | ||
577 | (child->flags & parentCannotResize_WidgetFlag) != 0); | ||
578 | } | ||
481 | } | 579 | } |
580 | TRACE(d, "...done resizing children to widest child"); | ||
482 | } | 581 | } |
483 | iInt2 pos = initv_I2(d->padding); | 582 | iInt2 pos = initv_I2(d->padding); |
583 | TRACE(d, "begin positioning children from %d,%d (flags:%s%s)...", pos.x, pos.y, | ||
584 | d->flags & arrangeHorizontal_WidgetFlag ? " horiz" : "", | ||
585 | d->flags & arrangeVertical_WidgetFlag ? " vert" : ""); | ||
484 | iForEach(ObjectList, i, d->children) { | 586 | iForEach(ObjectList, i, d->children) { |
485 | iWidget *child = as_Widget(i.object); | 587 | iWidget *child = as_Widget(i.object); |
486 | arrange_Widget(child); | 588 | arrange_Widget_(child); |
487 | if (!isArranged_Widget_(child)) { | 589 | if (!isArranged_Widget_(child)) { |
590 | TRACE(d, "child %p arranging prohibited", child); | ||
488 | continue; | 591 | continue; |
489 | } | 592 | } |
490 | if (child->flags & centerHorizontal_WidgetFlag) { | 593 | if (child->flags & centerHorizontal_WidgetFlag) { |
594 | TRACE(d, "child %p is centered, skipping", child); | ||
491 | continue; | 595 | continue; |
492 | } | 596 | } |
493 | if (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag)) { | 597 | if (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag)) { |
494 | if (child->flags & | 598 | if (child->flags & |
495 | (moveToParentLeftEdge_WidgetFlag | moveToParentRightEdge_WidgetFlag)) { | 599 | (moveToParentLeftEdge_WidgetFlag | moveToParentRightEdge_WidgetFlag)) { |
600 | TRACE(d, "child %p is attached an edge, skipping", child); | ||
496 | continue; /* Not part of the sequential arrangement .*/ | 601 | continue; /* Not part of the sequential arrangement .*/ |
497 | } | 602 | } |
498 | child->rect.pos = pos; | 603 | child->rect.pos = pos; |
604 | TRACE(d, "child %p set position to %d,%d", child, pos.x, pos.y); | ||
499 | if (d->flags & arrangeHorizontal_WidgetFlag) { | 605 | if (d->flags & arrangeHorizontal_WidgetFlag) { |
500 | pos.x += child->rect.size.x; | 606 | pos.x += child->rect.size.x; |
501 | } | 607 | } |
@@ -506,30 +612,20 @@ void arrange_Widget(iWidget *d) { | |||
506 | else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag && | 612 | else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag && |
507 | ~child->flags & moveToParentBottomEdge_WidgetFlag) { | 613 | ~child->flags & moveToParentBottomEdge_WidgetFlag) { |
508 | child->rect.pos = pos; | 614 | child->rect.pos = pos; |
615 | TRACE(d, "child %p set position to %d,%d (not sequential, children being resized)", child, pos.x, pos.y); | ||
509 | } | 616 | } |
510 | else if (d->flags & resizeWidthOfChildren_WidgetFlag) { | 617 | else if (d->flags & resizeWidthOfChildren_WidgetFlag) { |
511 | child->rect.pos.x = pos.x; | 618 | child->rect.pos.x = pos.x; |
619 | TRACE(d, "child %p set X to %d (not sequential, children being resized)", child, pos.x); | ||
512 | } | 620 | } |
513 | } | 621 | } |
622 | TRACE(d, "...done positioning children"); | ||
514 | /* Update the size of the widget according to the arrangement. */ | 623 | /* Update the size of the widget according to the arrangement. */ |
515 | if (d->flags & arrangeSize_WidgetFlag) { | 624 | if (d->flags & arrangeSize_WidgetFlag) { |
516 | iRect bounds = zero_Rect(); | 625 | iRect bounds; |
517 | iConstForEach(ObjectList, i, d->children) { | 626 | boundsOfChildren_Widget_(d, &bounds); |
518 | const iWidget *child = constAs_Widget(i.object); | 627 | TRACE(d, "begin arranging own size; bounds of children: %d,%d %dx%d", |
519 | if (isCollapsed_Widget_(child)) { | 628 | bounds.pos.x, bounds.pos.y, bounds.size.x, bounds.size.y); |
520 | continue; | ||
521 | } | ||
522 | iRect childRect = child->rect; | ||
523 | if (child->flags & ignoreForParentWidth_WidgetFlag) { | ||
524 | childRect.size.x = 0; | ||
525 | } | ||
526 | if (isEmpty_Rect(bounds)) { | ||
527 | bounds = childRect; | ||
528 | } | ||
529 | else { | ||
530 | bounds = union_Rect(bounds, childRect); | ||
531 | } | ||
532 | } | ||
533 | adjustEdges_Rect(&bounds, -d->padding[1], d->padding[2], d->padding[3], -d->padding[0]); | 629 | adjustEdges_Rect(&bounds, -d->padding[1], d->padding[2], d->padding[3], -d->padding[0]); |
534 | if (d->flags & arrangeWidth_WidgetFlag) { | 630 | if (d->flags & arrangeWidth_WidgetFlag) { |
535 | setWidth_Widget_(d, bounds.size.x); | 631 | setWidth_Widget_(d, bounds.size.x); |
@@ -539,14 +635,15 @@ void arrange_Widget(iWidget *d) { | |||
539 | if (child->flags & | 635 | if (child->flags & |
540 | (resizeToParentWidth_WidgetFlag | | 636 | (resizeToParentWidth_WidgetFlag | |
541 | moveToParentLeftEdge_WidgetFlag | | 637 | moveToParentLeftEdge_WidgetFlag | |
542 | moveToParentBottomEdge_WidgetFlag | | ||
543 | moveToParentRightEdge_WidgetFlag)) { | 638 | moveToParentRightEdge_WidgetFlag)) { |
544 | arrange_Widget(child); | 639 | TRACE(d, "rearranging child %p because its size or position depends on parent width", child); |
640 | arrange_Widget_(child); | ||
545 | } | 641 | } |
546 | } | 642 | } |
547 | if (d->flags & moveToParentRightEdge_WidgetFlag) { | 643 | if (d->flags & moveToParentRightEdge_WidgetFlag) { |
548 | /* TODO: Fix this: not DRY. See beginning of method. */ | 644 | /* TODO: Fix this: not DRY. See beginning of method. */ |
549 | d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); | 645 | d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); |
646 | TRACE(d, "after width change moving to right edge of parent, set X to %d", d, d->rect.pos.x); | ||
550 | } | 647 | } |
551 | } | 648 | } |
552 | if (d->flags & arrangeHeight_WidgetFlag) { | 649 | if (d->flags & arrangeHeight_WidgetFlag) { |
@@ -554,8 +651,10 @@ void arrange_Widget(iWidget *d) { | |||
554 | /* Parent size changed, must update the children.*/ | 651 | /* Parent size changed, must update the children.*/ |
555 | iForEach(ObjectList, j, d->children) { | 652 | iForEach(ObjectList, j, d->children) { |
556 | iWidget *child = as_Widget(j.object); | 653 | iWidget *child = as_Widget(j.object); |
557 | if (child->flags & resizeToParentHeight_WidgetFlag) { | 654 | if (child->flags & (resizeToParentHeight_WidgetFlag | |
558 | arrange_Widget(child); | 655 | moveToParentBottomEdge_WidgetFlag)) { |
656 | TRACE(d, "rearranging child %p because its size or position depends on parent height", child); | ||
657 | arrange_Widget_(child); | ||
559 | } | 658 | } |
560 | } | 659 | } |
561 | } | 660 | } |
@@ -566,9 +665,31 @@ void arrange_Widget(iWidget *d) { | |||
566 | if (d->flags & centerHorizontal_WidgetFlag) { | 665 | if (d->flags & centerHorizontal_WidgetFlag) { |
567 | centerHorizontal_Widget_(d); | 666 | centerHorizontal_Widget_(d); |
568 | } | 667 | } |
668 | TRACE(d, "...done arranging own size"); | ||
669 | } | ||
670 | TRACE(d, "END"); | ||
671 | } | ||
672 | |||
673 | void resetSize_Widget(iWidget *d) { | ||
674 | if (~d->flags & fixedWidth_WidgetFlag) { | ||
675 | d->rect.size.x = d->minSize.x; | ||
676 | } | ||
677 | if (~d->flags & fixedHeight_WidgetFlag) { | ||
678 | d->rect.size.y = d->minSize.y; | ||
679 | } | ||
680 | iForEach(ObjectList, i, children_Widget(d)) { | ||
681 | iWidget *child = as_Widget(i.object); | ||
682 | if (isArranged_Widget_(child)) { | ||
683 | resetSize_Widget(child); | ||
684 | } | ||
569 | } | 685 | } |
570 | } | 686 | } |
571 | 687 | ||
688 | void arrange_Widget(iWidget *d) { | ||
689 | //resetSize_Widget_(d); /* back to initial default sizes */ | ||
690 | arrange_Widget_(d); | ||
691 | } | ||
692 | |||
572 | static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { | 693 | static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { |
573 | if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { | 694 | if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { |
574 | const int off = iRound(value_Anim(&d->visualOffset)); | 695 | const int off = iRound(value_Anim(&d->visualOffset)); |
@@ -1354,6 +1475,7 @@ void refresh_Widget(const iAnyObject *d) { | |||
1354 | postRefresh_App(); | 1475 | postRefresh_App(); |
1355 | } | 1476 | } |
1356 | 1477 | ||
1478 | /* Debug utility for inspecting widget trees. */ | ||
1357 | #include "labelwidget.h" | 1479 | #include "labelwidget.h" |
1358 | static void printTree_Widget_(const iWidget *d, int indent) { | 1480 | static void printTree_Widget_(const iWidget *d, int indent) { |
1359 | for (int i = 0; i < indent; ++i) { | 1481 | for (int i = 0; i < indent; ++i) { |
@@ -1365,9 +1487,17 @@ static void printTree_Widget_(const iWidget *d, int indent) { | |||
1365 | cstr_String(text_LabelWidget((const iLabelWidget *) d)), | 1487 | cstr_String(text_LabelWidget((const iLabelWidget *) d)), |
1366 | cstr_String(command_LabelWidget((const iLabelWidget *) d))); | 1488 | cstr_String(command_LabelWidget((const iLabelWidget *) d))); |
1367 | } | 1489 | } |
1368 | printf("size:%dx%d [%d..%d %d:%d] flags:%08llx%s\n", d->rect.size.x, d->rect.size.y, | 1490 | printf("size:%dx%d {min:%dx%d} [%d..%d %d:%d] flags:%08llx%s%s%s%s%s\n", |
1369 | d->padding[0], d->padding[2], d->padding[1], d->padding[3], | 1491 | d->rect.size.x, d->rect.size.y, |
1370 | (long long unsigned int) d->flags, d->flags & tight_WidgetFlag ? " tight" : ""); | 1492 | d->minSize.x, d->minSize.y, |
1493 | d->padding[0], d->padding[2], | ||
1494 | d->padding[1], d->padding[3], | ||
1495 | (long long unsigned int) d->flags, | ||
1496 | d->flags & expand_WidgetFlag ? " exp" : "", | ||
1497 | d->flags & tight_WidgetFlag ? " tight" : "", | ||
1498 | d->flags & fixedWidth_WidgetFlag ? " fixW" : "", | ||
1499 | d->flags & fixedHeight_WidgetFlag ? " fixH" : "", | ||
1500 | d->flags & resizeToParentWidth_WidgetFlag ? " rsPrnW" : ""); | ||
1371 | iConstForEach(ObjectList, i, d->children) { | 1501 | iConstForEach(ObjectList, i, d->children) { |
1372 | printTree_Widget_(i.object, indent + 1); | 1502 | printTree_Widget_(i.object, indent + 1); |
1373 | } | 1503 | } |