MingaEditor.Layout (Minga v0.1.0)

Copy Markdown View Source

Single source of truth for all screen rectangles.

Layout.compute/1 takes editor state and produces a struct of named rectangles for every UI element. The renderer never computes its own coordinates; it receives pre-computed rectangles and draws into them.

Rectangle format

All rectangles are {row, col, width, height} tuples matching the existing WindowTree.rect() type. The origin is the top-left corner of the allocated area.

Regions vs Overlays

Regions are non-overlapping areas that tile the screen: file tree, editor area, agent panel, modeline, minibuffer. They participate in the non-overlap invariant.

Overlays float over regions: picker, which-key popup, completion menu. They have positioning rects but don't participate in the tiling constraint.

Summary

Types

A horizontal separator between split panes: {row, col, width, filename}.

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

t()

Complete layout for one frame.

Layout for a single editor window, with sub-rects for each chrome element.

Functions

Returns the content height (visible rows) for the active window.

Returns the content width for the active window.

Returns the window layout for the active window, or nil.

Splits a window layout's content rect to carve out a sidebar.

Computes the complete layout for the current frame.

Computes window layouts and horizontal separator positions simultaneously.

Returns the cached layout from state, or computes it fresh.

Invalidates the cached layout. Call when layout-affecting state changes (viewport resize, file tree toggle, agent panel toggle, window split/close).

Computes the layout and stores it in state for reuse within the same frame.

Types

horizontal_separator()

@type horizontal_separator() ::
  {non_neg_integer(), non_neg_integer(), pos_integer(), String.t()}

A horizontal separator between split panes: {row, col, width, filename}.

rect()

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

t()

@type t() :: %MingaEditor.Layout{
  agent_panel: rect() | nil,
  editor_area: rect(),
  file_tree: rect() | nil,
  horizontal_separators: [horizontal_separator()],
  minibuffer: rect(),
  status_bar: rect() | nil,
  tab_bar: rect() | nil,
  terminal: rect(),
  window_layouts: %{required(MingaEditor.Window.id()) => window_layout()}
}

Complete layout for one frame.

window_layout()

@type window_layout() :: %{
  total: rect(),
  content: rect(),
  modeline: {non_neg_integer(), non_neg_integer(), pos_integer(), 0},
  sidebar: rect() | nil
}

Layout for a single editor window, with sub-rects for each chrome element.

  • total — the full window rect (from WindowTree.layout)
  • content — the text area within the window (full window; no per-window modeline)
  • modeline — always zero-height; kept for backward compatibility. The global status bar at Layout.t().status_bar replaces per-window modelines.
  • sidebar — optional info panel (agent chat dashboard)

Functions

active_content_height(layout, state)

@spec active_content_height(t(), MingaEditor.State.t()) :: pos_integer()

Returns the content height (visible rows) for the active window.

active_content_width(layout, state)

@spec active_content_width(t(), MingaEditor.State.t()) :: pos_integer()

Returns the content width for the active window.

This is useful for computing gutter width and wrap maps before rendering.

active_window_layout(layout, state)

@spec active_window_layout(t(), MingaEditor.State.t()) :: window_layout() | nil

Returns the window layout for the active window, or nil.

add_sidebar(layout)

@spec add_sidebar(window_layout()) :: window_layout()

Splits a window layout's content rect to carve out a sidebar.

Returns the layout with content narrowed and sidebar set to the right-hand info panel rect. Only applied when the content width exceeds @sidebar_threshold.

compute(state)

@spec compute(MingaEditor.State.t() | map()) :: t()

Computes the complete layout for the current frame.

Delegates to the shell's compute_layout callback, which dispatches to the appropriate TUI or GUI layout module.

compute_window_layouts_with_separators(tree, editor_area, window_map)

@spec compute_window_layouts_with_separators(
  MingaEditor.WindowTree.t(),
  rect(),
  map()
) ::
  {%{required(MingaEditor.Window.id()) => window_layout()},
   [horizontal_separator()]}

Computes window layouts and horizontal separator positions simultaneously.

Horizontal splits steal 1 row from the top window for a separator bar. The separator shows the lower window's buffer filename.

Returns {window_layouts_map, horizontal_separators_list}.

get(state)

@spec get(MingaEditor.State.t() | map()) :: t()

Returns the cached layout from state, or computes it fresh.

Prefer this over compute/1 when you have a state that might already have a cached layout. The cache is invalidated on resize, file tree toggle, and agent panel toggle.

invalidate(state)

@spec invalidate(MingaEditor.State.t()) :: MingaEditor.State.t()

Invalidates the cached layout. Call when layout-affecting state changes (viewport resize, file tree toggle, agent panel toggle, window split/close).

put(state)

@spec put(MingaEditor.State.t() | map()) :: map()

Computes the layout and stores it in state for reuse within the same frame.

Call this once at the start of a render cycle or event handler, then read state.layout downstream.