← All 41 libraries

CandyLayout

🍬 CandyLayout

Constraint-based layout solver for terminal grids

inspired by ratatui/ratatui layout constraint-solver foundation-package

CandyLayout code coverage

A ratatui-style constraint layout solver behind a one-method LayoutSolver interface: GreedySolver (deterministic 5-phase solver ported from candy-sprinkles) implements every constraint type, and the deprecated CassowarySolver shim delegates to it. Plus DockLayout, a center-plus-side-stacks pane geometry. Foundation for candy-sprinkles, sugar-bits, and candy-forms layout.

Install

composer require sugarcraft/candy-layout

Quickstart

use SugarCraft\Layout\Constraint\Constraint;
use SugarCraft\Layout\{Direction, GreedySolver, Region};

$solver = GreedySolver::new();
$region = Region::fromSize(100, 24);

// The widths in the comments are what this 100-cell split returns.
$rects = $solver->solve($region, Direction::Horizontal, [
    Constraint::length(20),       // 20: exactly 20 cells
    Constraint::min(10),          // 10: a floor; the Fills below take the slack
    Constraint::percentage(25),   // 25: 25% of 100
    Constraint::ratio(1, 10),     // 10: 1/10 of 100
    Constraint::fill(1),          // 12: a third of the 35 cells left (+1 rounding cell, first Fill)
    Constraint::fill(2),          // 23: two thirds of them
]);

// Max is a greedy ceiling: it grows into free space up to its cap.
$capped = $solver->solve($region, Direction::Horizontal, [
    Constraint::length(20),       // 20
    Constraint::max(15),          // 15: clamped at its ceiling
    Constraint::fill(1),          // 65: everything Max did not take
]);

Solvers

GreedySolverDeterministic 5-phase solver. Bit-identical output to the existing candy-sprinkles Solver. No deps, no edit variables. Fast and predictable. Lossless rounding: floored fractions are handed back so the regions tile the span (Fill/Max remainder to the first Fill, Min slack residue to the first Min, a Percentage/Ratio shortfall of at most N−1 cells reclaimed earliest-first). When fixed demand exceeds the span, regions shrink proportionally and still sum to it — withoutOverflowTruncation() keeps full base sizes instead.
CassowarySolver (deprecated)The original Big-M simplex prototype never converged, so solve() raises E_USER_DEPRECATED and delegates wholly to GreedySolver — results are identical. Kept only so existing call-sites keep working.
DockLayoutCenter pane plus left/right weighted slot stacks. Stack heights are split in exact integer arithmetic (no float decides a boundary row); a weight set whose scaled sum would overflow is refused with InvalidArgumentException where it is introduced, and resolve() itself never throws, degrading side by side on small frames.

Constraint types

LengthFixed cell count — takes exactly n cells.
MinFloor — at least n cells. Grows only when no Fill or Max takes the slack, or when a Max hands cells back.
MaxCeiling — greedy, clamped to n cells.
FillProportional remainder — weight controls distribution across multiple Fill constraints.
PercentageFraction of total — n% of the available dimension.
RatioFractional proportion — num/denom of the total span, reserved like a fixed size.

Use it for

Source & demos

Try the quickstart →

API

ClassMethodDescription
LayoutSolversolve(Region, Direction, list<Constraint>)Solve constraints against a region in the given direction; returns list of sub-regions
GreedySolvernew(): self / compat(): selfDefault factory / sugar-boxer parity mode (round-split, remainder-to-last, non-truncating overflow)
GreedySolverwithRoundSplit() / withRemainderToLast() / withoutOverflowTruncation() / withMinShare(cells, reserveGap, reserveLead)Immutable rounding and overflow options
GreedySolver / CassowarySolverstatic greedy() / static cassowary()Concrete-class factories (no longer declared on the LayoutSolver interface)
CassowarySolvernew(): selfDeprecated shim; solve() raises E_USER_DEPRECATED and delegates to GreedySolver
DirectionHorizontal, VerticalEnum: split direction
RegionfromSize(int, int)Factory: width, height
Constraintlength(int)Fixed cell count
Constraintmin(int)Floor — at least n cells
Constraintmax(int)Ceiling — at most n cells
Constraintfill(int $weight)Proportional remainder with weight
Constraintpercentage(int 0-100)Percentage of total
Constraintratio(int $num, int $denom)Fractional proportion of the total span