ExRatatui.Layout (ExRatatui v0.11.0)

Copy Markdown View Source

Layout system for splitting areas into sub-regions.

Uses ratatui's constraint-based layout engine to divide a Rect into multiple sub-regions along a direction.

Constraints

  • {:percentage, n} - percentage of the total space
  • {:length, n} - exact number of cells
  • {:min, n} - minimum number of cells
  • {:max, n} - maximum number of cells
  • {:ratio, numerator, denominator} - fractional ratio
  • {:fill, weight} - proportional share of the remaining space after higher-priority constraints (Length/Percentage/Ratio/etc.) are satisfied. {:fill, 1} + {:fill, 2} splits leftover space in a 1:2 ratio. Useful for "growable" panels in dashboards.

Options

split/4 accepts a fourth keyword-list argument:

  • :flex — how excess space is distributed when constraints don't fill the area. One of:
    • :legacy — pre-Flex behaviour; last constraint absorbs excess
    • :start — pack from the start
    • :end — pack from the end
    • :center — pack centered, excess split between both ends
    • :space_between — distribute excess between segments
    • :space_around — distribute excess around every segment Defaults to ratatui's Flex::default() (currently :start).
  • :spacing — non-negative integer cells inserted between every pair of adjacent segments. Defaults to 0.
  • :margin — non-negative integer cells inset on all four sides of the area before it is split. Defaults to 0.
  • :horizontal_margin / :vertical_margin — per-axis inset that overrides :margin for that axis when given. Use these for asymmetric insets (e.g. horizontal_margin: 2, vertical_margin: 1).

Examples

area = %Rect{x: 0, y: 0, width: 80, height: 24}

# Split the area but leave a 1-cell border around the edges.
[body] = Layout.split(area, :vertical, [{:min, 0}], margin: 1)

area = %Rect{x: 0, y: 0, width: 80, height: 24}

[header, body, footer] = Layout.split(area, :vertical, [
  {:length, 3},
  {:min, 0},
  {:length, 1}
])

[sidebar, main] = Layout.split(body, :horizontal, [
  {:percentage, 30},
  {:percentage, 70}
])

# Centered popup with 40-cell fixed width
[popup] = Layout.split(area, :horizontal, [{:length, 40}], flex: :center)

# Two equal-weight growable panels with a 2-cell gutter
[left, right] = Layout.split(area, :horizontal, [{:fill, 1}, {:fill, 1}],
  spacing: 2)

Summary

Functions

Splits a Rect into sub-regions based on direction and constraints.

Types

constraint()

@type constraint() ::
  {:percentage, non_neg_integer()}
  | {:length, non_neg_integer()}
  | {:min, non_neg_integer()}
  | {:max, non_neg_integer()}
  | {:ratio, non_neg_integer(), non_neg_integer()}
  | {:fill, non_neg_integer()}

direction()

@type direction() :: :horizontal | :vertical

flex()

@type flex() :: :legacy | :start | :end | :center | :space_between | :space_around

split_opt()

@type split_opt() ::
  {:flex, flex()}
  | {:spacing, non_neg_integer()}
  | {:margin, non_neg_integer()}
  | {:horizontal_margin, non_neg_integer()}
  | {:vertical_margin, non_neg_integer()}

Functions

split(area, direction, constraints, opts \\ [])

@spec split(ExRatatui.Layout.Rect.t(), direction(), [constraint()], [split_opt()]) ::
  [ExRatatui.Layout.Rect.t()] | {:error, term()}

Splits a Rect into sub-regions based on direction and constraints.

Accepts an optional 4th argument with :flex and :spacing options (see module doc). Returns a list of %Rect{} structs or {:error, reason} on failure.

Examples

iex> alias ExRatatui.Layout
iex> alias ExRatatui.Layout.Rect
iex> area = %Rect{x: 0, y: 0, width: 80, height: 24}
iex> [top, bottom] = Layout.split(area, :vertical, [{:percentage, 50}, {:percentage, 50}])
iex> top
%Rect{x: 0, y: 0, width: 80, height: 12}
iex> bottom
%Rect{x: 0, y: 12, width: 80, height: 12}

iex> alias ExRatatui.Layout
iex> alias ExRatatui.Layout.Rect
iex> area = %Rect{x: 0, y: 0, width: 100, height: 1}
iex> [left, right] = Layout.split(area, :horizontal, [{:length, 20}, {:min, 0}])
iex> left
%Rect{x: 0, y: 0, width: 20, height: 1}
iex> right
%Rect{x: 20, y: 0, width: 80, height: 1}

iex> alias ExRatatui.Layout
iex> alias ExRatatui.Layout.Rect
iex> area = %Rect{x: 0, y: 0, width: 30, height: 1}
iex> [popup] = Layout.split(area, :horizontal, [{:length, 10}], flex: :center)
iex> popup
%Rect{x: 10, y: 0, width: 10, height: 1}

iex> alias ExRatatui.Layout
iex> alias ExRatatui.Layout.Rect
iex> area = %Rect{x: 0, y: 0, width: 22, height: 1}
iex> [left, right] = Layout.split(area, :horizontal, [{:length, 10}, {:length, 10}], spacing: 2)
iex> left.width == 10 and right.x == 12 and right.width == 10
true