box method

  1. @override
Vector2 box({
  1. required Iterable<LayoutItem> items,
  2. required LayoutConstraints constraints,
  3. required double? targetWidth,
  4. required double? targetHeight,
  5. required EdgeInsets padding,
  6. required Anchor? alignment,
})
override

Sizes a region to targetWidth/targetHeight - or to its largest item plus padding where either is null - then places every item inside it at alignment.

Example (no alignment)

Let's say you have a 100x60 region with 10 of padding and no alignment, holding one resizable item.

+----------------------------------------+
|                                        |
|    +--------------------------------+  |
|    |              item              |  |
|    +--------------------------------+  |
|                                        |
+----------------------------------------+
0   10                              90 100

The steps are as follows:

  1. Narrow the constraints. The padding leaves an 80x40 interior, and nothing loosens it, so its minimum carries through.
  2. Measure every item against that. Forced to fill, the item reports 80x40.
  3. Size the region. With no target it shrink-wraps to the largest item plus padding, landing on 100x60.
  4. Place every item on the padded origin, (10, 10).

Example (alignment)

Now the same region, aligned at (0.5, 0.5), holding one 20x20 item.

+----------------------------------------+
|                                        |
|                +------+                |
|                | item |                |
|                +------+                |
|                                        |
+----------------------------------------+
0                40      60            100

The steps are as follows:

  1. Narrow the constraints. The padding leaves an 80x40 interior, and aligning loosens it, so the item may come back smaller.
  2. Measure every item against that. The item reports 20x20.
  3. Size the region. Aligning asks for room to align within, so each bounded axis is taken whole, landing on 100x60.
  4. Place every item. That leaves 60x20 of room inside the padding, and half of it puts the item at (40, 20).

Implementation

@override
Vector2 box({
  required Iterable<LayoutItem> items,
  required LayoutConstraints constraints,
  required double? targetWidth,
  required double? targetHeight,
  required EdgeInsets padding,
  required Anchor? alignment,
}) {
  final LayoutConstraints region;

  if (targetWidth != null || targetHeight != null) {
    region = LayoutConstraints(
      min: .new(targetWidth ?? 0, targetHeight ?? 0),
      max: .new(targetWidth ?? double.infinity, targetHeight ?? double.infinity),
    ).enforce(constraints);
  } else {
    region = constraints;
  }

  var itemConstraints = region;

  if (padding != .zero) {
    itemConstraints = itemConstraints.deflate(padding);
  }

  if (alignment != null) {
    itemConstraints = itemConstraints.loosen();
  }

  // Measure, in each item's own space, comparing the extent back in this one.
  final largest = MVector2.zero();
  final extent = MVector2.zero();

  for (final item in items) {
    item.layout(itemConstraints.descale(item.scale));

    extent
      ..setFrom(item.size)
      ..multiply(item.scale)
      ..absolute();

    largest.max(extent);
  }

  // Size. An alignment takes every bounded axis whole; a target still wins.
  final fill = alignment != null;
  final size = region.constrain(
    fill && region.hasBoundedWidth ? region.max.x : largest.x + padding.horizontal,
    fill && region.hasBoundedHeight ? region.max.y : largest.y + padding.vertical,
  );

  // Place. An item still reports the size it measured at.
  final anchor = alignment ?? .topLeft;
  final innerWidth = size.x - padding.horizontal;
  final innerHeight = size.y - padding.vertical;

  for (final item in items) {
    extent
      ..setFrom(item.size)
      ..multiply(item.scale)
      ..absolute();

    place(
      item,
      .new(
        padding.left + (innerWidth - extent.x) * anchor.x,
        padding.top + (innerHeight - extent.y) * anchor.y,
      ),
    );
  }

  return size;
}