[ Concepts ]
Declarative Layout and Constraints
The idea in plain words
Section titled “The idea in plain words”Terminal windows can be resized at any time. Instead of hardcoding (row, col) coordinates, you start from frame.area() and subdivide rectangles vertically, horizontally, or into a 2D Grid using Constraint::fixed(n), Constraint::percent(n), and Constraint::fill().
How koto implements it
Section titled “How koto implements it”- Two-pass 1D constraint solver (
split_1d): BothLayout::verticalandLayout::horizontalcallsplit_1d(src/layout.cpp:8-70):- Pass 1: Sums
c.valueforConstraint::Type::Fixedand(total_size * c.value) / 100forConstraint::Type::Percentintoused, and countsConstraint::Type::Fillentries infill_count. - Remainder calculation: Computes
remaining = std::max(0, total_size - used),fill_size = remaining / fill_count, andfill_remainder = remaining % fill_count. - Pass 2: Assigns each segment its size (giving the first
fill_remainderfill segmentsfill_size + 1and the restfill_size), clampssize = std::min(size, std::max(0, (origin + total_size) - current_pos)), appends theAreatoLayoutResult, and advancescurrent_pos += size.
- Pass 1: Sums
- Small Buffer Optimization in
LayoutResult:LayoutResultembedsstd::array<Area, 16> m_stack(include/koto/layout.hpp:112, 180). As long as a layout split has<= 16constraints,push_backwrites solely intom_stackwith zero heap allocation. Only when a 17th constraint is pushed doesLayoutResultcopym_stackintostd::vector<Area> m_heap(include/koto/layout.hpp:124-129). - Compile-time array overloads for structured bindings: When you pass a braced array literal
Layout::vertical(area, {Constraint::fixed(3), Constraint::fill()}), C++ deducestemplate <size_t N> std::array<Area, N> vertical(Area, const Constraint (&)[N])(include/koto/layout.hpp:249-258), allowing C++17 structured bindings (auto [top, bottom] = ...).
auto [top_bar, main_pane, status_bar] = Layout::vertical(frame.area(), { Constraint::fixed(3), Constraint::fill(), Constraint::fixed(1),});static_cast<void>(top_bar);static_cast<void>(main_pane);static_cast<void>(status_bar);Consequences for your code
Section titled “Consequences for your code”- Use
area.inset(1, 1, 1, 1)inside bordered boxes: ABoxwith a visible border occupies 1 row on top and bottom and 1 column on left and right. Callbox_area.inset(1, 1, 1, 1)to get the interiorareafor child widgets. - Note that
Layout::gridallocates astd::vector<Area>: While 1DLayout::verticalandLayout::horizontalare zero-allocation up to 16 constraints,Gridstores its cells instd::vector<Area> m_cells(include/koto/layout.hpp:237).