summaryrefslogtreecommitdiff
path: root/src/ui/widget.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-30 12:17:07 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-30 12:17:07 +0300
commit1495d08163a4bfcd5db5f6c3b06f031032c5461f (patch)
tree7538e5ca6f054afd292ca9c4b7b7ff81a3d1222f /src/ui/widget.c
parentfed2b149aeb5c5cd692421602e0fe77ceffb8b28 (diff)
Revising and fixing widget layout
Some of the logic for arranging widgets was invalid, leading to problems with the navbar: - cannot resize children if own size depends on their size - expanding children won't expand unless resizing all children
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r--src/ui/widget.c161
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
235iLocalDef iBool isCollapsed_Widget_(const iWidget *d) {
236 return (d->flags & (hidden_WidgetFlag | collapse_WidgetFlag)) ==
237 (hidden_WidgetFlag | collapse_WidgetFlag);
238}
239
240iLocalDef iBool isArrangedPos_Widget_(const iWidget *d) {
241 return (d->flags & fixedPosition_WidgetFlag) == 0;
242}
243
244iLocalDef iBool isArrangedSize_Widget_(const iWidget *d) {
245 return !isCollapsed_Widget_(d) && isArrangedPos_Widget_(d) &&
246 !(d->flags & parentCannotResize_WidgetFlag);
247}
248
249iLocalDef iBool doesAffectSizing_Widget_(const iWidget *d) {
250 return !isCollapsed_Widget_(d) && isArrangedPos_Widget_(d);
251}
252
235static int numExpandingChildren_Widget_(const iWidget *d) { 253static 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
255static void arrange_Widget_(iWidget *); 273static void arrange_Widget_(iWidget *);
256static const iBool tracing_ = iFalse; 274static 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
330iLocalDef iBool isCollapsed_Widget_(const iWidget *d) {
331 return (d->flags & (hidden_WidgetFlag | collapse_WidgetFlag)) ==
332 (hidden_WidgetFlag | collapse_WidgetFlag);
333}
334
335iLocalDef iRect innerRect_Widget_(const iWidget *d) { 348iLocalDef 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
350iLocalDef 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
354static size_t numArrangedChildren_Widget_(const iWidget *d) { 368static 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
392static void arrange_Widget_(iWidget *d) { 416static 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
651void resetSize_Widget(iWidget *d) { 693void 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
666void arrange_Widget(iWidget *d) { 709void arrange_Widget(iWidget *d) {
667 //resetSize_Widget_(d); /* back to initial default sizes */ 710 //resetSize_Widget_(d); /* back to initial default sizes */