# `MingaEditor.WindowTree`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/window_tree.ex#L1)

A binary tree representing the spatial layout of editor windows.

Each leaf is a window id. Each branch is a horizontal or vertical split
containing two subtrees. The tree is used to compute screen regions for
each window and to navigate between them directionally.

## Structure

    {:leaf, window_id}
    {:split, :vertical, left_tree, right_tree}
    {:split, :horizontal, top_tree, bottom_tree}

Vertical splits produce side-by-side panes. Horizontal splits produce
stacked panes.

# `direction`

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

Split direction.

# `nav_direction`

```elixir
@type nav_direction() :: :left | :right | :up | :down
```

Navigation direction for focus movement.

# `rect`

```elixir
@type rect() :: {non_neg_integer(), non_neg_integer(), pos_integer(), pos_integer()}
```

A screen rectangle: {row, col, width, height}.

# `t`

```elixir
@type t() ::
  {:leaf, MingaEditor.Window.id()}
  | {:split, direction(), t(), t(), non_neg_integer()}
```

The tree structure.

Split nodes carry a `size` — the number of columns (vertical) or rows
(horizontal) allocated to the first child. The second child gets the
remainder minus any separator. A size of 0 means "split evenly" and is
resolved during layout.

# `clamp_size`

```elixir
@spec clamp_size(non_neg_integer(), pos_integer()) :: pos_integer()
```

Resolves a split size: 0 means "half", otherwise clamp to [1, total-1].

Used by layout and renderer to consistently compute child dimensions.

# `close`

```elixir
@spec close(t(), MingaEditor.Window.id()) :: {:ok, t()} | :error
```

Removes the leaf containing `window_id` from the tree.

The sibling subtree takes the removed node's place. Returns `:error` if
the tree is a single leaf (cannot close the last window) or if the id
is not found.

# `count`

```elixir
@spec count(t()) :: pos_integer()
```

Returns the number of leaves (windows) in the tree.

# `focus_neighbor`

```elixir
@spec focus_neighbor(t(), MingaEditor.Window.id(), nav_direction(), rect()) ::
  {:ok, MingaEditor.Window.id()} | :error
```

Finds the neighbor window id when moving from `from_id` in the given direction.

Uses the layout to determine spatial adjacency. Returns `{:ok, neighbor_id}`
or `:error` if there is no neighbor in that direction.

# `layout`

```elixir
@spec layout(t(), rect()) :: [{MingaEditor.Window.id(), rect()}]
```

Computes the screen rectangle for each leaf window.

Given the total available rect `{row, col, width, height}`, recursively
splits the space according to the tree structure. Vertical splits divide
width (with 1 column reserved for the separator). Horizontal splits
divide height.

Returns a list of `{window_id, {row, col, width, height}}`.

# `leaves`

```elixir
@spec leaves(t()) :: [MingaEditor.Window.id()]
```

Returns all window ids in the tree, left-to-right / top-to-bottom order.

# `member?`

```elixir
@spec member?(t(), MingaEditor.Window.id()) :: boolean()
```

Returns true if the given window id exists in the tree.

# `new`

```elixir
@spec new(MingaEditor.Window.id()) :: t()
```

Creates a tree with a single window.

# `resize_at`

```elixir
@spec resize_at(t(), rect(), direction(), non_neg_integer(), non_neg_integer()) ::
  {:ok, t()} | :error
```

Resizes the split that owns the separator at `separator_pos` to place
that separator at `new_pos`. Only resizes vertical splits (by column)
for now.

Returns `{:ok, new_tree}` or `:error` if no matching split is found.

# `separator_at`

```elixir
@spec separator_at(t(), rect(), non_neg_integer(), non_neg_integer()) ::
  {:ok, {direction(), non_neg_integer()}} | :error
```

Tests whether a screen coordinate is on a separator.

Returns `{:ok, :vertical | :horizontal, separator_position}` or `:error`.
For vertical splits, `separator_position` is the column; for horizontal,
it's the row. The position is used to identify which split node to resize.

# `split`

```elixir
@spec split(t(), MingaEditor.Window.id(), direction(), MingaEditor.Window.id()) ::
  {:ok, t()} | :error
```

Splits the leaf containing `window_id` in the given direction.

The existing window stays in the first position (left/top) and
the new window takes the second position (right/bottom).

Returns `{:ok, new_tree}` or `:error` if `window_id` is not found.

# `window_at`

```elixir
@spec window_at(t(), rect(), non_neg_integer(), non_neg_integer()) ::
  {:ok, MingaEditor.Window.id(), rect()} | :error
```

Finds which window contains the given screen coordinate.

Returns `{:ok, window_id, {row, col, width, height}}` or `:error` if the
coordinate is outside any window rect (e.g. on a separator).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
