diff options
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r-- | src/ui/widget.c | 161 |
1 files changed, 102 insertions, 59 deletions
diff --git a/src/ui/widget.c b/src/ui/widget.c index e4d92b35..62112e86 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -232,11 +232,29 @@ void setRoot_Widget(iWidget *d, iRoot *root) { | |||
232 | } | 232 | } |
233 | } | 233 | } |
234 | 234 | ||
235 | iLocalDef iBool isCollapsed_Widget_(const iWidget *d) { | ||
236 | return (d->flags & (hidden_WidgetFlag | collapse_WidgetFlag)) == | ||
237 | (hidden_WidgetFlag | collapse_WidgetFlag); | ||
238 | } | ||
239 | |||
240 | iLocalDef iBool isArrangedPos_Widget_(const iWidget *d) { | ||
241 | return (d->flags & fixedPosition_WidgetFlag) == 0; | ||
242 | } | ||
243 | |||
244 | iLocalDef iBool isArrangedSize_Widget_(const iWidget *d) { | ||
245 | return !isCollapsed_Widget_(d) && isArrangedPos_Widget_(d) && | ||
246 | !(d->flags & parentCannotResize_WidgetFlag); | ||
247 | } | ||
248 | |||
249 | iLocalDef iBool doesAffectSizing_Widget_(const iWidget *d) { | ||
250 | return !isCollapsed_Widget_(d) && isArrangedPos_Widget_(d); | ||
251 | } | ||
252 | |||
235 | static int numExpandingChildren_Widget_(const iWidget *d) { | 253 | static int numExpandingChildren_Widget_(const iWidget *d) { |
236 | int count = 0; | 254 | int count = 0; |
237 | iConstForEach(ObjectList, i, d->children) { | 255 | iConstForEach(ObjectList, i, d->children) { |
238 | const iWidget *child = constAs_Widget(i.object); | 256 | const iWidget *child = constAs_Widget(i.object); |
239 | if (flags_Widget(child) & expand_WidgetFlag) { | 257 | if (flags_Widget(child) & expand_WidgetFlag && doesAffectSizing_Widget_(child)) { |
240 | count++; | 258 | count++; |
241 | } | 259 | } |
242 | } | 260 | } |
@@ -253,7 +271,7 @@ static int widestChild_Widget_(const iWidget *d) { | |||
253 | } | 271 | } |
254 | 272 | ||
255 | static void arrange_Widget_(iWidget *); | 273 | static void arrange_Widget_(iWidget *); |
256 | static const iBool tracing_ = iFalse; | 274 | static const iBool tracing_ = 0; |
257 | 275 | ||
258 | #define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); } | 276 | #define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); } |
259 | 277 | ||
@@ -288,7 +306,7 @@ static void setWidth_Widget_(iWidget *d, int width) { | |||
288 | iAssert(width >= 0); | 306 | iAssert(width >= 0); |
289 | TRACE(d, "attempt to set width to %d (current: %d, min width: %d)", width, d->rect.size.x, d->minSize.x); | 307 | TRACE(d, "attempt to set width to %d (current: %d, min width: %d)", width, d->rect.size.x, d->minSize.x); |
290 | width = iMax(width, d->minSize.x); | 308 | width = iMax(width, d->minSize.x); |
291 | if (~d->flags & fixedWidth_WidgetFlag || d->flags & collapse_WidgetFlag) { | 309 | if (~d->flags & fixedWidth_WidgetFlag) { //} || d->flags & collapse_WidgetFlag) { |
292 | if (d->rect.size.x != width) { | 310 | if (d->rect.size.x != width) { |
293 | d->rect.size.x = width; | 311 | d->rect.size.x = width; |
294 | TRACE(d, "width has changed to %d", width); | 312 | TRACE(d, "width has changed to %d", width); |
@@ -313,7 +331,7 @@ static void setHeight_Widget_(iWidget *d, int height) { | |||
313 | iAssert(height >= 0); | 331 | iAssert(height >= 0); |
314 | TRACE(d, "attempt to set height to %d (current: %d, min height: %d)", height, d->rect.size.y, d->minSize.y); | 332 | TRACE(d, "attempt to set height to %d (current: %d, min height: %d)", height, d->rect.size.y, d->minSize.y); |
315 | height = iMax(height, d->minSize.y); | 333 | height = iMax(height, d->minSize.y); |
316 | if (~d->flags & fixedHeight_WidgetFlag || d->flags & collapse_WidgetFlag) { | 334 | if (~d->flags & fixedHeight_WidgetFlag) { //} || d->flags & collapse_WidgetFlag) { |
317 | if (d->rect.size.y != height) { | 335 | if (d->rect.size.y != height) { |
318 | d->rect.size.y = height; | 336 | d->rect.size.y = height; |
319 | TRACE(d, "height has changed to %d", height); | 337 | TRACE(d, "height has changed to %d", height); |
@@ -327,11 +345,6 @@ static void setHeight_Widget_(iWidget *d, int height) { | |||
327 | } | 345 | } |
328 | } | 346 | } |
329 | 347 | ||
330 | iLocalDef iBool isCollapsed_Widget_(const iWidget *d) { | ||
331 | return (d->flags & (hidden_WidgetFlag | collapse_WidgetFlag)) == | ||
332 | (hidden_WidgetFlag | collapse_WidgetFlag); | ||
333 | } | ||
334 | |||
335 | iLocalDef iRect innerRect_Widget_(const iWidget *d) { | 348 | iLocalDef iRect innerRect_Widget_(const iWidget *d) { |
336 | return init_Rect(d->padding[0], | 349 | return init_Rect(d->padding[0], |
337 | d->padding[1], | 350 | d->padding[1], |
@@ -347,14 +360,15 @@ iRect innerBounds_Widget(const iWidget *d) { | |||
347 | return ib; | 360 | return ib; |
348 | } | 361 | } |
349 | 362 | ||
350 | iLocalDef iBool isArranged_Widget_(const iWidget *d) { | 363 | //iLocalDef iBool isArranged_Widget_(const iWidget *d) { |
351 | return !isCollapsed_Widget_(d) && ~d->flags & fixedPosition_WidgetFlag; | 364 | // return !isCollapsed_Widget_(d) && ~d->flags & fixedPosition_WidgetFlag; |
352 | } | 365 | //} |
366 | |||
353 | 367 | ||
354 | static size_t numArrangedChildren_Widget_(const iWidget *d) { | 368 | static size_t numArrangedChildren_Widget_(const iWidget *d) { |
355 | size_t count = 0; | 369 | size_t count = 0; |
356 | iConstForEach(ObjectList, i, d->children) { | 370 | iConstForEach(ObjectList, i, d->children) { |
357 | if (isArranged_Widget_(i.object)) { | 371 | if (isArrangedPos_Widget_(i.object)) { |
358 | count++; | 372 | count++; |
359 | } | 373 | } |
360 | } | 374 | } |
@@ -387,15 +401,20 @@ static void boundsOfChildren_Widget_(const iWidget *d, iRect *bounds_out) { | |||
387 | *bounds_out = union_Rect(*bounds_out, childRect); | 401 | *bounds_out = union_Rect(*bounds_out, childRect); |
388 | } | 402 | } |
389 | } | 403 | } |
404 | #if !defined (NDEBUG) | ||
405 | if (tracing_) { | ||
406 | if (bounds_out->size.x && bounds_out->size.y == 0) { | ||
407 | printf("SUSPECT CHILD BOUNDS?\n"); | ||
408 | puts ("---------------------"); | ||
409 | printTree_Widget(d); | ||
410 | puts ("---------------------"); | ||
411 | } | ||
412 | } | ||
413 | #endif | ||
390 | } | 414 | } |
391 | 415 | ||
392 | static void arrange_Widget_(iWidget *d) { | 416 | static void arrange_Widget_(iWidget *d) { |
393 | TRACE(d, "arranging..."); | 417 | TRACE(d, "arranging..."); |
394 | if (isCollapsed_Widget_(d)) { | ||
395 | TRACE(d, "collapsed => END"); | ||
396 | setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); | ||
397 | return; | ||
398 | } | ||
399 | if (d->flags & moveToParentLeftEdge_WidgetFlag) { | 418 | if (d->flags & moveToParentLeftEdge_WidgetFlag) { |
400 | d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */ | 419 | d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */ |
401 | TRACE(d, "move to parent left edge: %d", d->rect.pos.x); | 420 | TRACE(d, "move to parent left edge: %d", d->rect.pos.x); |
@@ -439,57 +458,42 @@ static void arrange_Widget_(iWidget *d) { | |||
439 | } | 458 | } |
440 | const size_t childCount = numArrangedChildren_Widget_(d); | 459 | const size_t childCount = numArrangedChildren_Widget_(d); |
441 | TRACE(d, "%d arranged children", childCount); | 460 | TRACE(d, "%d arranged children", childCount); |
461 | const int expCount = numExpandingChildren_Widget_(d); | ||
462 | TRACE(d, "%d expanding children", expCount); | ||
442 | /* Resize children to fill the parent widget. */ | 463 | /* Resize children to fill the parent widget. */ |
443 | if (d->flags & resizeChildren_WidgetFlag) { | 464 | if (d->flags & resizeChildren_WidgetFlag) { |
444 | const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0, | 465 | const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0, |
445 | (d->flags & resizeHeightOfChildren_WidgetFlag) != 0); | 466 | (d->flags & resizeHeightOfChildren_WidgetFlag) != 0); |
446 | TRACE(d, "resize children, x:%d y:%d", dirs.x, dirs.y); | 467 | #if !defined (NDEBUG) |
447 | /* Collapse hidden children. */ | 468 | /* Check for conflicting flags. */ |
448 | iBool collapseChanged = iFalse; | 469 | if (dirs.x) iAssert(~d->flags & arrangeWidth_WidgetFlag); |
449 | iForEach(ObjectList, c, d->children) { | 470 | if (dirs.y) iAssert(~d->flags & arrangeHeight_WidgetFlag); |
450 | iWidget *child = as_Widget(c.object); | 471 | #endif |
451 | if (!isCollapsed_Widget_(child) && child->flags & wasCollapsed_WidgetFlag) { | 472 | TRACE(d, "resize children, x:%d y:%d (own size: %dx%d)", dirs.x, dirs.y, |
452 | setFlags_Widget(child, wasCollapsed_WidgetFlag, iFalse); | 473 | d->rect.size.x, d->rect.size.y); |
453 | TRACE(d, "child %p is uncollapsed", child); | ||
454 | /* Undo collapse and determine the normal size again. */ | ||
455 | arrange_Widget_(d); | ||
456 | collapseChanged = iTrue; | ||
457 | } | ||
458 | else if (isCollapsed_Widget_(child) && ~child->flags & wasCollapsed_WidgetFlag) { | ||
459 | setFlags_Widget(child, wasCollapsed_WidgetFlag, iTrue); | ||
460 | collapseChanged = iTrue; | ||
461 | TRACE(d, "child %p flagged as collapsed", child); | ||
462 | } | ||
463 | } | ||
464 | if (collapseChanged) { | ||
465 | TRACE(d, "redoing arrangement due to changes in child collapse state"); | ||
466 | arrange_Widget_(d); /* Redo with the new child sizes. */ | ||
467 | return; | ||
468 | } | ||
469 | const int expCount = numExpandingChildren_Widget_(d); | ||
470 | TRACE(d, "%d expanding children", expCount); | ||
471 | /* Only resize the expanding children, not touching the others. */ | ||
472 | if (expCount > 0) { | 474 | if (expCount > 0) { |
475 | /* There are expanding children, so all non-expanding children will retain their | ||
476 | current size. */ | ||
473 | iInt2 avail = innerRect_Widget_(d).size; | 477 | iInt2 avail = innerRect_Widget_(d).size; |
474 | TRACE(d, "inner size: %dx%d", avail.x, avail.y); | 478 | TRACE(d, "inner size: %dx%d", avail.x, avail.y); |
475 | iConstForEach(ObjectList, i, d->children) { | 479 | iConstForEach(ObjectList, i, d->children) { |
476 | const iWidget *child = constAs_Widget(i.object); | 480 | const iWidget *child = constAs_Widget(i.object); |
477 | if (!isArranged_Widget_(child)) { | 481 | if (doesAffectSizing_Widget_(child)) { |
478 | continue; | 482 | if (~child->flags & expand_WidgetFlag) { |
479 | } | 483 | subv_I2(&avail, child->rect.size); |
480 | if (~child->flags & expand_WidgetFlag) { | 484 | } |
481 | subv_I2(&avail, child->rect.size); | ||
482 | } | 485 | } |
483 | } | 486 | } |
484 | avail = divi_I2(max_I2(zero_I2(), avail), expCount); | 487 | avail = divi_I2(max_I2(zero_I2(), avail), expCount); |
485 | TRACE(d, "changing child sizes (expand mode)..."); | 488 | TRACE(d, "changing child sizes..."); |
486 | iForEach(ObjectList, j, d->children) { | 489 | iForEach(ObjectList, j, d->children) { |
487 | iWidget *child = as_Widget(j.object); | 490 | iWidget *child = as_Widget(j.object); |
488 | if (!isArranged_Widget_(child)) { | 491 | if (!isArrangedSize_Widget_(child)) { |
489 | TRACE(d, "child %p is not arranged", child); | 492 | TRACE(d, "child %p size is not arranged", child); |
490 | continue; | 493 | continue; |
491 | } | 494 | } |
492 | if (child->flags & expand_WidgetFlag) { | 495 | if (~child->flags & expand_WidgetFlag) { |
496 | #if 0 | ||
493 | if (d->flags & arrangeHorizontal_WidgetFlag) { | 497 | if (d->flags & arrangeHorizontal_WidgetFlag) { |
494 | if (dirs.x) setWidth_Widget_(child, avail.x); | 498 | if (dirs.x) setWidth_Widget_(child, avail.x); |
495 | if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d))); | 499 | if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d))); |
@@ -500,6 +504,7 @@ static void arrange_Widget_(iWidget *d) { | |||
500 | } | 504 | } |
501 | } | 505 | } |
502 | else { | 506 | else { |
507 | #endif | ||
503 | /* Fill the off axis, though. */ | 508 | /* Fill the off axis, though. */ |
504 | if (d->flags & arrangeHorizontal_WidgetFlag) { | 509 | if (d->flags & arrangeHorizontal_WidgetFlag) { |
505 | if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d))); | 510 | if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d))); |
@@ -509,7 +514,7 @@ static void arrange_Widget_(iWidget *d) { | |||
509 | } | 514 | } |
510 | } | 515 | } |
511 | } | 516 | } |
512 | TRACE(d, "...done changing child sizes (expand mode)"); | 517 | TRACE(d, "...done changing child sizes"); |
513 | } | 518 | } |
514 | else { | 519 | else { |
515 | /* Evenly size all children. */ | 520 | /* Evenly size all children. */ |
@@ -526,7 +531,7 @@ static void arrange_Widget_(iWidget *d) { | |||
526 | TRACE(d, "begin changing child sizes (EVEN mode)..."); | 531 | TRACE(d, "begin changing child sizes (EVEN mode)..."); |
527 | iForEach(ObjectList, i, d->children) { | 532 | iForEach(ObjectList, i, d->children) { |
528 | iWidget *child = as_Widget(i.object); | 533 | iWidget *child = as_Widget(i.object); |
529 | if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { | 534 | if (isArrangedSize_Widget_(child)) { |
530 | if (dirs.x) { | 535 | if (dirs.x) { |
531 | setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x); | 536 | setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x); |
532 | } | 537 | } |
@@ -535,19 +540,55 @@ static void arrange_Widget_(iWidget *d) { | |||
535 | } | 540 | } |
536 | } | 541 | } |
537 | else { | 542 | else { |
538 | TRACE(d, "child %p cannot be resized (parentCannotResize: %d)", child, | 543 | TRACE(d, "child %p cannot be resized (collapsed: %d, arrangedPos: %d, parentCannotResize: %d)", child, |
544 | isCollapsed_Widget_(child), | ||
545 | isArrangedPos_Widget_(child), | ||
539 | (child->flags & parentCannotResize_WidgetFlag) != 0); | 546 | (child->flags & parentCannotResize_WidgetFlag) != 0); |
540 | } | 547 | } |
541 | } | 548 | } |
542 | TRACE(d, "...done changing child sizes (EVEN mode)"); | 549 | TRACE(d, "...done changing child sizes (EVEN mode)"); |
543 | } | 550 | } |
544 | } | 551 | } |
552 | /* Resize the expanding children to fill the remaining available space. */ | ||
553 | if (expCount > 0 && (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag))) { | ||
554 | TRACE(d, "%d expanding children, resizing them %s...", expCount, | ||
555 | d->flags & arrangeHorizontal_WidgetFlag ? "horizontally" : "vertically"); | ||
556 | const iRect innerRect = innerRect_Widget_(d); | ||
557 | iInt2 avail = innerRect.size; | ||
558 | iConstForEach(ObjectList, i, d->children) { | ||
559 | const iWidget *child = constAs_Widget(i.object); | ||
560 | if (doesAffectSizing_Widget_(child)) { | ||
561 | if (~child->flags & expand_WidgetFlag) { | ||
562 | subv_I2(&avail, child->rect.size); | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | avail = divi_I2(max_I2(zero_I2(), avail), expCount); | ||
567 | TRACE(d, "available for expansion (per child): %d\n", d->flags & arrangeHorizontal_WidgetFlag ? avail.x : avail.y); | ||
568 | iForEach(ObjectList, j, d->children) { | ||
569 | iWidget *child = as_Widget(j.object); | ||
570 | if (!isArrangedSize_Widget_(child)) { | ||
571 | TRACE(d, "child %p size is not arranged", child); | ||
572 | continue; | ||
573 | } | ||
574 | if (child->flags & expand_WidgetFlag) { | ||
575 | if (d->flags & arrangeHorizontal_WidgetFlag) { | ||
576 | setWidth_Widget_(child, avail.x); | ||
577 | setHeight_Widget_(child, height_Rect(innerRect)); | ||
578 | } | ||
579 | else if (d->flags & arrangeVertical_WidgetFlag) { | ||
580 | setWidth_Widget_(child, width_Rect(innerRect)); | ||
581 | setHeight_Widget_(child, avail.y); | ||
582 | } | ||
583 | } | ||
584 | } | ||
585 | } | ||
545 | if (d->flags & resizeChildrenToWidestChild_WidgetFlag) { | 586 | if (d->flags & resizeChildrenToWidestChild_WidgetFlag) { |
546 | const int widest = widestChild_Widget_(d); | 587 | const int widest = widestChild_Widget_(d); |
547 | TRACE(d, "resizing children to widest child (%d)...", widest); | 588 | TRACE(d, "resizing children to widest child (%d)...", widest); |
548 | iForEach(ObjectList, i, d->children) { | 589 | iForEach(ObjectList, i, d->children) { |
549 | iWidget *child = as_Widget(i.object); | 590 | iWidget *child = as_Widget(i.object); |
550 | if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { | 591 | if (isArrangedSize_Widget_(child)) { |
551 | setWidth_Widget_(child, widest); | 592 | setWidth_Widget_(child, widest); |
552 | } | 593 | } |
553 | else { | 594 | else { |
@@ -564,7 +605,7 @@ static void arrange_Widget_(iWidget *d) { | |||
564 | iForEach(ObjectList, i, d->children) { | 605 | iForEach(ObjectList, i, d->children) { |
565 | iWidget *child = as_Widget(i.object); | 606 | iWidget *child = as_Widget(i.object); |
566 | arrange_Widget_(child); | 607 | arrange_Widget_(child); |
567 | if (!isArranged_Widget_(child)) { | 608 | if (isCollapsed_Widget_(child) || !isArrangedPos_Widget_(child)) { |
568 | TRACE(d, "child %p arranging prohibited", child); | 609 | TRACE(d, "child %p arranging prohibited", child); |
569 | continue; | 610 | continue; |
570 | } | 611 | } |
@@ -648,6 +689,7 @@ static void arrange_Widget_(iWidget *d) { | |||
648 | TRACE(d, "END"); | 689 | TRACE(d, "END"); |
649 | } | 690 | } |
650 | 691 | ||
692 | #if 0 | ||
651 | void resetSize_Widget(iWidget *d) { | 693 | void resetSize_Widget(iWidget *d) { |
652 | if (~d->flags & fixedWidth_WidgetFlag) { | 694 | if (~d->flags & fixedWidth_WidgetFlag) { |
653 | d->rect.size.x = d->minSize.x; | 695 | d->rect.size.x = d->minSize.x; |
@@ -657,11 +699,12 @@ void resetSize_Widget(iWidget *d) { | |||
657 | } | 699 | } |
658 | iForEach(ObjectList, i, children_Widget(d)) { | 700 | iForEach(ObjectList, i, children_Widget(d)) { |
659 | iWidget *child = as_Widget(i.object); | 701 | iWidget *child = as_Widget(i.object); |
660 | if (isArranged_Widget_(child)) { | 702 | if (isArrangedSize_Widget_(child)) { |
661 | resetSize_Widget(child); | 703 | resetSize_Widget(child); |
662 | } | 704 | } |
663 | } | 705 | } |
664 | } | 706 | } |
707 | #endif | ||
665 | 708 | ||
666 | void arrange_Widget(iWidget *d) { | 709 | void arrange_Widget(iWidget *d) { |
667 | //resetSize_Widget_(d); /* back to initial default sizes */ | 710 | //resetSize_Widget_(d); /* back to initial default sizes */ |