MingaEditor.WindowTree (Minga v0.1.0)

Copy Markdown View Source

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.

Summary

Types

Split direction.

Navigation direction for focus movement.

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

t()

The tree structure.

Functions

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

Removes the leaf containing window_id from the tree.

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

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

Computes the screen rectangle for each leaf window.

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

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

Creates a tree with a single window.

Resets the split that owns separator_pos to equal halves.

Resets the split separator under the exact screen coordinate to equal halves.

Resizes the split that owns the separator at separator_pos to place that separator at new_pos.

Tests whether a screen coordinate is on a separator.

Splits the leaf containing window_id in the given direction.

Finds which window contains the given screen coordinate.

Types

direction()

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

Split direction.

nav_direction()

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

Navigation direction for focus movement.

rect()

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

t()

@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.

Functions

clamp_size(size, total)

@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(tree, target)

@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(arg)

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

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

focus_neighbor(tree, from_id, direction, screen_rect)

@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(tree, rect)

@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(arg)

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

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

member?(arg, id)

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

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

new(id)

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

Creates a tree with a single window.

reset_split_at(tree, screen_rect, direction, separator_pos)

@spec reset_split_at(t(), rect(), direction(), non_neg_integer()) ::
  {:ok, t()} | :error

Resets the split that owns separator_pos to equal halves.

The stored split size is set to 0, which clamp_size/2 resolves to an even split during layout. If multiple separators share the same row or column, this returns :error rather than guessing.

reset_split_at_coordinate(tree, screen_rect, row, col)

@spec reset_split_at_coordinate(t(), rect(), non_neg_integer(), non_neg_integer()) ::
  {:ok, t()} | :error

Resets the split separator under the exact screen coordinate to equal halves.

Use this for mouse interactions because multiple separators can share the same row or column in different panes.

resize_at(tree, screen_rect, direction, separator_pos, new_pos)

@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.

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

separator_at(tree, screen_rect, row, col)

@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(tree, target_id, direction, new_id)

@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(tree, screen_rect, row, col)

@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).