altui 0.2.0

A state-driven TUI runtime built on top of altui-core
Documentation
//! # altui
//!
//! `altui` is a state-driven TUI runtime built around [`View`].
//!
//! It separates a terminal application into explicit parts:
//!
//! - global application state via [`AppHandler`]
//! - a stable collection of views via [`ViewFactory`]
//! - per-frame area assignment via [`CtxStore`]
//! - the event/render runtime via [`ViewLoop`]
//!
//! The central idea is simple: every visible behavior in the UI should be
//! explainable in terms of view state, layout assignment, and one shared app state.
//!
//! ## Mental Model
//!
//! A [`View`] is a state-aware UI unit that can:
//!
//! - react to events through [`View::on_event`]
//! - update itself through [`View::logic`]
//! - render into a buffer through [`View::render`]
//!
//! A frame in `altui` is built from two coordinated closures:
//!
//! 1. the `views` closure inserts views into [`ViewFactory`]
//! 2. the `areas` closure assigns [`Rect`] values to those views through [`CtxStore`]
//!
//! These two sequences must stay in sync.
//!
//! ## Core Rule: View Count Must Match Area Count
//!
//! The runtime relies on one invariant:
//!
//! - the number of inserted views must always match the number of assigned areas
//!
//! In practice this means:
//!
//! - the `views` closure defines the order of views
//! - the `areas` closure runs every frame and must assign areas in the same order
//! - even hidden views still need an area, often via [`EMPTY_AREA`] or [`CtxStore::skip_areas`]
//!
//! This is why the [`simple_pages` example](https://altlinux.space/writers/altui/src/branch/main/examples/simple_pages)
//! can hide whole pages while still keeping the runtime stable.
//!
//! ## View States
//!
//! A view can be in one of three runtime states:
//!
//! - **inactive**
//! - **hovered**
//! - **active**
//!
//! A view context can also be configured as:
//!
//! - visible or hidden
//! - selectable
//! - interactive
//! - button-like
//!
//! These flags influence navigation and event routing.
//!
//! The most useful mental categories are:
//!
//! - **regular view**: rendered only, not part of navigation
//! - **selectable view**: participates in hover navigation and may perform an extra action when it briefly becomes active
//! - **interactive view**: remains active and keeps receiving input until it exits the active state
//! - **button view**: becomes active for one iteration and then returns to hover automatically
//!
//! A subtle but important detail:
//!
//! - both selectable and interactive views can receive [`View::on_event`]
//! - a selectable view does not take over the whole input flow; it can add behavior on top of normal navigation
//! - an interactive view captures input while active, so you usually need to exit `active` before moving to another interactive view
//!
//! This distinction shows up clearly in the examples:
//!
//! - [`scroll_paragraph`](https://altlinux.space/writers/altui/src/branch/main/examples/scroll_paragraph) uses a selectable view to add local scrolling behavior
//! - [`popup`](https://altlinux.space/writers/altui/src/branch/main/examples/popup) uses an interactive view that keeps focus while the popup is open
//! - [`flex_buttons`](https://altlinux.space/writers/altui/src/branch/main/examples/flex_buttons) and [`simple_buttons_with_vscroll`](https://altlinux.space/writers/altui/src/branch/main/examples/simple_buttons_with_vscroll) use button views
//!
//! ## Read the Examples First
//!
//! The root examples are the main source of truth for how `altui` is intended to be used.
//!
//! Recommended reading order:
//!
//! - [`scroll_paragraph`](https://altlinux.space/writers/altui/src/branch/main/examples/scroll_paragraph):
//!   one custom view, one local scroll state, two widgets rendered by one view
//! - [`flex`](https://altlinux.space/writers/altui/src/branch/main/examples/flex):
//!   basic area assignment with [`CtxStore::split`] and `Layout::flex`
//! - [`flex_buttons`](https://altlinux.space/writers/altui/src/branch/main/examples/flex_buttons):
//!   button views that update shared layout state
//! - [`simple_buttons_with_vscroll`](https://altlinux.space/writers/altui/src/branch/main/examples/simple_buttons_with_vscroll):
//!   two-dimensional keyboard navigation with `set_vscroll`
//! - [`simple_pages`](https://altlinux.space/writers/altui/src/branch/main/examples/simple_pages):
//!   conditional visibility and [`CtxStore::skip_areas`]
//! - [`popup`](https://altlinux.space/writers/altui/src/branch/main/examples/popup):
//!   layered UI, visibility toggling, and focus transfer
//! - [`scroll_layout`](https://altlinux.space/writers/altui/src/branch/main/examples/scroll_layout):
//!   external scrolling with [`CtxStore::split_ext`]
//! - [`wrap`](https://altlinux.space/writers/altui/src/branch/main/examples/wrap) and
//!   [`wrap_with_scroll`](https://altlinux.space/writers/altui/src/branch/main/examples/wrap_with_scroll):
//!   wrapped layouts with and without scrolling
//! - [`mouse_drag_border`](https://altlinux.space/writers/altui/src/branch/main/examples/mouse_drag_border):
//!   mouse-driven layout updates
//!
//! The README files in those example directories summarize the main behavior, but the
//! source files are the most important reference.
//!
//! ## Minimal Example
//!
//! ```rust,ignore
//! use altui::{
//!     backend::Backend,
//!     buffer::Buffer,
//!     layout::{Alignment, Constraint, Flex, Layout, Rect},
//!     widgets::{Paragraph, Widget},
//!     AltuiInit, AppHandler, AreaCache, CtxStore, Terminal, View, ViewFactory, ViewLoop,
//! };
//! use crossterm::event::{read, Event, KeyCode};
//!
//! struct HelloView<'a> {
//!     text: Paragraph<'a>,
//! }
//!
//! impl<'a> HelloView<'a> {
//!     fn new() -> Self {
//!         let mut text = Paragraph::new("Hello world!");
//!         text.alignment(Alignment::Center);
//!         Self { text }
//!     }
//! }
//!
//! impl View for HelloView<'_> {
//!     type State = MainState;
//!
//!     fn render(&mut self, area: Rect, _: &mut AreaCache, buf: &mut Buffer) {
//!         self.text.render(area, buf);
//!     }
//! }
//!
//! struct MainState {}
//!
//! impl AppHandler for MainState {}
//!
//! impl MainState {
//!     fn new() -> Self {
//!         Self {}
//!     }
//!
//!     fn run<'a, B: Backend>(mut self, terminal: &mut Terminal<B>) {
//!         ViewLoop::new(terminal).unwrap().basic_navigation(true).run(
//!             &mut self,
//!             |factory: &mut ViewFactory<MainState>, _: &mut MainState| {
//!                 factory.insert_view(HelloView::new());
//!             },
//!             |area: Rect, ctx: &mut CtxStore, _: &mut MainState| {
//!                 let layout = ctx.split(
//!                     Layout::vertical(&[Constraint::Length(1)]).flex(Flex::Center),
//!                     area,
//!                 );
//!                 ctx.next_areas(&layout);
//!             },
//!             read,
//!             |state, factory| {
//!                 if let Some(Event::Key(key)) = factory.event() {
//!                     match key.code {
//!                         KeyCode::Esc | KeyCode::Char('q') => state.stop(),
//!                         _ => {}
//!                     }
//!                 }
//!             },
//!         );
//!     }
//! }
//!
//! fn main() -> std::io::Result<()> {
//!     AltuiInit::new(true)?.set_panic_hook().run(|terminal| {
//!         MainState::new().run(terminal);
//!         Ok(())
//!     })
//! }
//! ```
//!
//! This is the smallest useful `altui` program:
//!
//! - one app state
//! - one custom view
//! - one area assignment
//! - one global event handler
//!
//! ## Architecture Overview
//!
//! `altui` follows a state-driven view architecture:
//!
//! ```text
//! ┌──────────────────────────────┐
//! │        Application State     │
//! │        (AppHandler)          │
//! └──────────────┬───────────────┘
//!//!                │ shared mutable state
//!//! ┌──────────────────────────────┐
//! │           ViewLoop           │
//! │  (runtime event/render loop) │
//! └──────────────┬───────────────┘
//!//!        manages │ views + contexts
//!//! ┌──────────────────────────────┐
//! │         ViewFactory          │
//! │ stores views and contexts    │
//! └──────────────┬───────────────┘
//!//!                │ executes
//!//!        ┌───────────────┐
//!        │     View      │
//!        │  (UI element) │
//!        └───────────────┘
//!//!                │ renders using
//!//!        ┌───────────────┐
//!        │   altui_core  │
//!        │ widgets/buffer│
//!        └───────────────┘
//! ```
//!
//! In this architecture:
//!
//! - [`AppHandler`] owns global application state and controls process lifetime
//! - [`ViewLoop`] orchestrates events, logic, area assignment, and rendering
//! - [`ViewFactory`] stores views and runtime contexts
//! - [`View`] defines local UI behavior
//!
//! Views do not own the app state directly. Instead, they receive mutable access
//! to it during logic and event processing.
//!
//! ## Event Lifecycle
//!
//! A frame processed by [`ViewLoop`] follows this sequence:
//!
//! ```text
//! 1. Read input event
//! 2. Run built-in navigation, if enabled
//! 3. Dispatch the event
//! 4. Run view logic
//! 5. Assign areas
//! 6. Render visible views
//! ```
//!
//! Event dispatch priority is:
//!
//! 1. active view
//! 2. hovered view
//! 3. global event handler
//!
//! This priority is why popups, editors, and mouse-driven widgets can temporarily
//! take over input handling without replacing the whole runtime.
//!
//! ## `View` vs `AnyView`
//!
//! There are two common ways to insert UI elements into `altui`.
//!
//! ### Implement `View`
//!
//! Implement [`View`] when you need:
//!
//! - internal state inside the view
//! - custom event handling
//! - multiple child widgets rendered from one view
//! - explicit hover/active behavior
//!
//! Typical cases:
//!
//! - the scrollable panel from [`scroll_paragraph`](https://altlinux.space/writers/altui/src/branch/main/examples/scroll_paragraph)
//! - the popup from [`popup`](https://altlinux.space/writers/altui/src/branch/main/examples/popup)
//! - custom mouse-driven components like [`mouse_drag_border`](https://altlinux.space/writers/altui/src/branch/main/examples/mouse_drag_border)
//!
//! ```rust,ignore
//! struct MyView { /* internal state */ }
//!
//! impl View for MyView {
//!     type State = AppState;
//!
//!     fn logic(&mut self, ctx: &mut ViewCtx, state: &mut AppState) {
//!         // update local state from app state
//!     }
//!
//!     fn on_event(&mut self, event: Event, ctx: &mut ViewCtx, state: &mut AppState) {
//!         // optional custom input handling
//!     }
//!
//!     fn render(&mut self, area: Rect, cache: &mut AreaCache, buf: &mut Buffer) {
//!         // render the view
//!     }
//! }
//! ```
//!
//! ### Use `AnyView`
//!
//! [`AnyView`] is the lightest way to insert a widget into the runtime.
//!
//! It is a good fit when:
//!
//! - the widget is mostly presentational
//! - state already lives in the application
//! - a small logic closure is enough
//!
//! ```rust,ignore
//! let paragraph = Paragraph::new("Hello world!");
//! factory.insert_view(AnyView::simple(paragraph));
//! ```
//!
//! `AnyView::new` is useful when the widget still needs a small amount of state-driven logic:
//!
//! ```rust,ignore
//! enum Pages {
//!     PageOne,
//!     PageTwo,
//! }
//!
//! let paragraph = Paragraph::new("Hello world!");
//! factory.insert_view(AnyView::new(
//!     paragraph,
//!     |_widget, ctx, state: &mut MainState| match state.pages {
//!         Pages::PageOne => ctx.set_visible(false),
//!         Pages::PageTwo => ctx.set_visible(true),
//!     },
//! ));
//! ```
//!
//! This pattern is used heavily in:
//!
//! - [`flex`](https://altlinux.space/writers/altui/src/branch/main/examples/flex)
//! - [`flex_buttons`](https://altlinux.space/writers/altui/src/branch/main/examples/flex_buttons)
//! - [`simple_buttons_with_vscroll`](https://altlinux.space/writers/altui/src/branch/main/examples/simple_buttons_with_vscroll)
//! - [`simple_pages`](https://altlinux.space/writers/altui/src/branch/main/examples/simple_pages)
//!
//! ## Layout Guidance
//!
//! In area-building code, prefer [`CtxStore::split`] and [`CtxStore::split_ext`]
//! over calling `Layout::split` directly.
//!
//! `CtxStore` owns the frame-level layout cache, and the examples in this repository
//! are written around that rule.
//!
//! In render code, use [`AreaCache`] when a single view needs its own cached internal layout.
//!
//! ## Reexports
//!
//! `altui` reexports everything from `altui_core`.
//!
//! It also reexports `altui-textarea`, an adapted fork of `tui-textarea`, from:
//!
//! - <https://altlinux.space/writers/altui/src/branch/main/widgets/tui-textarea>
//!
//! The fork keeps the `tui-textarea` API shape, but is published separately so
//! it is clear that it is not the upstream crate.
//!
//! That fork was adjusted specifically to work better with `altui`. It may
//! later be proposed upstream, but for now it should be treated as part of this
//! repository's current architecture.
//!
//! ## Recommendations
//!
//! Use `ratatui` if:
//!
//! - you want a mature widget ecosystem
//! - you need a more stable public API
//! - you prefer ecosystem familiarity over experimentation
//!
//! Use `altui` if:
//!
//! - you want explicit control over view state and event routing
//! - you want cached layout assignment built into the runtime
//! - you are comfortable with an experimental API that is still being shaped by examples
mod app_handler;
mod context;
mod ctxstore;
mod event_loop;
mod factory;
mod ids;
mod views;

pub use crate::{
    app_handler::AppHandler,
    context::ViewCtx,
    ctxstore::{AreaCache, CtxStore, EMPTY_AREA},
    event_loop::ViewLoop,
    factory::ViewFactory,
    ids::WidgetId,
};

pub use altui_core::*;

pub use tui_textarea::*;

pub use crate::views::*;