wijjit.elements.input.text.TextArea

class wijjit.elements.input.text.TextArea(id=None, classes=None, tab_index=None, value='', width=40, height=10, wrap_mode='none', max_lines=None, show_scrollbar=True, show_scrollbar_x=False, border_style='single', action=None, bind=True, dynamic_sizing=False, autosize=False, max_height=None, capture_tab=False, tab_width=4)[source]

Multiline text area element with scrolling support.

Parameters:
  • id (str, optional) – Element identifier

  • value (str, optional) – Initial value (multiline text with n separators)

  • width (int, optional) – Display width in columns for content area (default: 40). Note: Borders add 2 additional columns to total width when enabled.

  • height (int, optional) – Display height in rows/lines (default: 10)

  • wrap_mode ({"none", "soft", "hard"}, optional) – Line wrapping mode (default: “none”) - “none”: No wrapping, lines can exceed width - “soft”: Visual wrapping only for display - “hard”: Insert actual newlines when line exceeds width

  • max_lines (int, optional) – Maximum number of lines allowed

  • show_scrollbar (bool, optional) – Whether to show vertical scrollbar (default: True)

  • border_style (BorderStyle or {"single", "double", "rounded"} or None, optional) – Border style for the text area (default: “single”). - “single”: Single-line box-drawing characters - “double”: Double-line box-drawing characters - “rounded”: Rounded corner box-drawing characters - None: No borders Can also accept BorderStyle enum values.

  • capture_tab (bool, optional) – Whether Tab inserts an indent instead of moving focus to the next element (default: False). Shift+Tab still moves focus backward, so the field can always be left. CodeEditor defaults this to True.

  • tab_width (int, optional) – Number of spaces one Tab inserts when capture_tab is enabled (default: 4). Minimum 1.

  • classes (str | list[str] | set[str] | None)

  • tab_index (int | None)

  • show_scrollbar_x (bool)

  • action (str | None)

  • bind (bool | str)

  • dynamic_sizing (bool)

  • autosize (bool)

  • max_height (int | None)

Variables:
  • lines (list of str) – Text content as array of lines

  • cursor_row (int) – Current cursor line (0-based)

  • cursor_col (int) – Current cursor column within line (0-based)

  • scroll_manager (ScrollManager) – Manages vertical scrolling

  • width (int) – Display width (content area, excluding borders)

  • height (int) – Display height (viewport)

  • wrap_mode (str) – Line wrapping mode

  • max_lines (int or None) – Maximum line count

  • show_scrollbar (bool) – Whether to show scrollbar

  • border_style (BorderStyle or None) – Border style for rendering

  • capture_tab (bool) – Whether Tab inserts an indent instead of moving focus

  • tab_width (int) – Number of spaces inserted by Tab when capture_tab is enabled

__init__(id=None, classes=None, tab_index=None, value='', width=40, height=10, wrap_mode='none', max_lines=None, show_scrollbar=True, show_scrollbar_x=False, border_style='single', action=None, bind=True, dynamic_sizing=False, autosize=False, max_height=None, capture_tab=False, tab_width=4)[source]
Parameters:

Methods

__init__([id, classes, tab_index, value, ...])

add_class(class_name)

Add a CSS class to this element.

apply_props(props)

Apply props from a VNode, skipping ephemeral props.

get_ephemeral_state()

Get ephemeral_state for reconciliation.

get_hardware_cursor_position()

Absolute screen cell where the hardware terminal cursor should park.

get_height_for_width(width)

Return the height this element needs when laid out at width.

get_intrinsic_size()

Get the intrinsic size of the text area, including borders.

get_value()

Get the full text content.

handle_key(key)

Handle keyboard input, recording undo history around any edit.

handle_mouse(event)

Handle mouse input.

has_class(class_name)

Check if element has a CSS class.

on_blur()

Called when element loses focus.

on_focus()

Called when element gains focus.

on_hover_enter()

Called when mouse enters element.

on_hover_exit()

Called when mouse exits element.

on_mount()

Called when element is first added to the element tree.

on_unmount()

Called when element is removed from the element tree.

on_update(changed_props)

Handle prop updates during reconciliation.

redo()

Re-apply the most recently undone edit.

remove_class(class_name)

Remove a CSS class from this element.

render_signature()

Hashable/comparable capture of everything render_to() reads.

render_to(ctx)

Render textarea using cell-based rendering (NEW API).

restore_ephemeral_state(state)

Restore ephemeral state after reconciliation.

restore_scroll_position_x(position)

Restore horizontal scroll position from saved state.

rewrap_content([new_width])

Explicitly re-wrap all content to a new width.

set_bounds(bounds)

Set bounds and dynamically resize if needed.

set_value(text)

Set the full text content.

toggle_class(class_name)

Toggle a CSS class on this element.

undo()

Revert the most recent edit.

Attributes

captures_tab

Whether Tab inserts an indent here instead of moving focus.

parent_frame

Get the parent Frame if this element is inside a scrollable frame.

scroll_position_x

Get the current horizontal scroll position.

supports_dynamic_sizing

Whether this TextArea supports dynamic sizing.

value

Full text content as a single newline-joined string.

property supports_dynamic_sizing: bool

Whether this TextArea supports dynamic sizing.

Returns:

True if configured with fill sizing, False otherwise

Return type:

bool

get_intrinsic_size()[source]

Get the intrinsic size of the text area, including borders.

When autosize is enabled the height grows to fit the current content (visual line count, wrapping-aware) up to max_height total rows; otherwise the configured height is reported.

Returns:

(width, height) where both include border characters.

Return type:

tuple[int, int]

set_bounds(bounds)[source]

Set bounds and dynamically resize if needed.

Parameters:

bounds (Bounds) – New bounds for the element

Return type:

None

property scroll_position_x: int

Get the current horizontal scroll position.

Returns:

Current horizontal scroll offset (0-based)

Return type:

int

restore_scroll_position_x(position)[source]

Restore horizontal scroll position from saved state.

Parameters:

position (int) – Horizontal scroll position to restore

Return type:

None

Notes

This method is called by the wiring system to restore scroll state between renders when wrap_mode=”none”.

property value: str

Full text content as a single newline-joined string.

Mirrors TextInput.value so element.value reads/writes uniformly across text inputs. Assignment delegates to set_value() (replacing all content and firing on_change); reading delegates to get_value().

get_value()[source]

Get the full text content.

Returns:

Complete text with newline separators

Return type:

str

set_value(text)[source]

Set the full text content.

Parameters:

text (str) – New text content (can contain newlines)

Return type:

None

Notes

This replaces all content and resets cursor to start. Updates scroll manager with new content size.

A programmatic replacement is a new document, so it discards undo history - but only when the text actually differs, so re-setting the text the element already holds is a true no-op.

The reconciler path is already safe without this check, which is worth knowing before anyone “simplifies” it: value is not an ephemeral prop, so apply_props re-assigns it on every render and a bound TextArea round-trips its own value (type -> state[key] = v -> re-render -> apply_props). But that goes through the value setter, which already skips set_value when the text is unchanged - it guards cursor and scroll against exactly the same round-trip. This check covers the direct caller instead: app code doing element.set_value(x) with the current text should not silently drop the user’s undo stack.

rewrap_content(new_width=None)[source]

Explicitly re-wrap all content to a new width.

Parameters:

new_width (int, optional) – New content width to wrap to. If None, uses current content width.

Return type:

None

Notes

Only applies when wrap_mode=”hard”. Does nothing for other wrap modes. Useful when terminal width changes and content needs to be re-wrapped. Attempts to preserve cursor position relative to content. Emits on_change callback if content changes.

undo()[source]

Revert the most recent edit.

Returns:

True if a state was restored, False if there was nothing to undo.

Return type:

bool

redo()[source]

Re-apply the most recently undone edit.

Returns:

True if a state was restored, False if there was nothing to redo.

Return type:

bool

property captures_tab: bool

Whether Tab inserts an indent here instead of moving focus.

Returns:

The value of capture_tab.

Return type:

bool

See also

wijjit.elements.base.Element.captures_tab

The framework contract.

handle_key(key)[source]

Handle keyboard input, recording undo history around any edit.

Parameters:

key (Key) – Key press to handle

Returns:

True if key was handled

Return type:

bool

Notes

Undo snapshots are taken here, at the key boundary, rather than inside the five mutating primitives (_insert_char, _insert_newline, _backspace, _delete, _delete_selection) and _paste. Those mutate self.lines independently and share no choke point, so hooking them would mean six hooks - and would produce worse semantics, because a single keypress can legitimately run several of them. Typing over a selection is a delete plus an insert; an insert in hard-wrap mode is an insert plus a reflow (_apply_hard_wrap_to_line). Snapshotting at this boundary makes each of those one undo unit, which is what a user expects, and costs one hook instead of six.

A contiguous run of character insertions coalesces into a single entry (tracked by _insert_run_end), so typing a word is one undo rather than one per letter. The run breaks on any other edit, on a cursor move, and on a selection change - all of which land the next insert somewhere other than where the last one ended.

async handle_mouse(event)[source]

Handle mouse input.

Parameters:

event (MouseEvent) – Mouse event to handle

Returns:

True if event was handled

Return type:

bool

Notes

Supports both vertical and horizontal scrolling: - Normal scroll wheel: vertical scrolling - Shift+scroll wheel: horizontal scrolling - Scroll over horizontal scrollbar area: horizontal scrolling

render_to(ctx)[source]

Render textarea using cell-based rendering (NEW API).

Parameters:

ctx (PaintContext) – Paint context with buffer, style resolver, and bounds

Return type:

None

Notes

This method implements cell-based rendering for text areas with: - Optional borders with focus indication - Multi-line text content - Cursor rendering with reverse video - Line wrapping (none and soft modes) - Scrollbar integration - Theme-based styling

Theme styles:

This element uses the following theme style classes: - textarea: Base textarea style - textarea:focus: When textarea has focus - textarea.border: Border characters - textarea.border:focus: Border when focused

on_update(changed_props)[source]

Handle prop updates during reconciliation.

Parameters:

changed_props (dict) – Map of prop_name -> (old_value, new_value)

Return type:

None

get_ephemeral_state()[source]

Get ephemeral_state for reconciliation.

Returns:

Cursor, selection, and scroll state that should survive re-renders

Return type:

dict

restore_ephemeral_state(state)[source]

Restore ephemeral state after reconciliation.

Parameters:

state (dict) – State from get_ephemeral_state()

Return type:

None