diff options
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 | } |