Skip to main content

AppContext

Struct AppContext 

Source
pub struct AppContext {
Show 35 fields pub arena: DenseSlotMap<WidgetId, Box<dyn Widget>>, pub children: SecondaryMap<WidgetId, Vec<WidgetId>>, pub parent: SecondaryMap<WidgetId, Option<WidgetId>>, pub computed_styles: SecondaryMap<WidgetId, ComputedStyle>, pub inline_styles: SecondaryMap<WidgetId, Vec<Declaration>>, pub dirty: SecondaryMap<WidgetId, bool>, pub pseudo_classes: SecondaryMap<WidgetId, PseudoClassSet>, pub focused_widget: Option<WidgetId>, pub hovered_widget: Option<WidgetId>, pub screen_stack: Vec<WidgetId>, pub focus_history: Vec<Option<WidgetId>>, pub pending_mounts: Vec<WidgetId>, pub input_buffer: String, pub event_tx: Option<Sender<AppEvent>>, pub message_queue: RefCell<Vec<(WidgetId, Box<dyn Any>)>>, pub pending_screen_pushes: RefCell<Vec<Box<dyn Widget>>>, pub pending_screen_pops: Cell<usize>, pub theme: Theme, pub stylesheets: Vec<Stylesheet>, pub worker_tx: Option<Sender<(WidgetId, Box<dyn Any + Send>)>>, pub worker_handles: RefCell<SecondaryMap<WidgetId, Vec<AbortHandle>>>, pub pending_recompose: RefCell<Vec<WidgetId>>, pub active_overlay: RefCell<Option<Box<dyn Widget>>>, pub pending_overlay_dismiss: Cell<bool>, pub terminal_caps: TerminalCaps, pub skip_animations: bool, pub mouse_capture_stack: MouseCaptureStack, pub pending_mouse_push: RefCell<Vec<bool>>, pub pending_mouse_pops: Cell<usize>, pub loading_widgets: RefCell<SecondaryMap<WidgetId, bool>>, pub spinner_tick: Cell<u8>, pub toast_entries: RefCell<Vec<ToastEntry>>, pub pending_screen_wait_pushes: RefCell<Vec<(Box<dyn Widget>, Sender<Box<dyn Any + Send>>)>>, pub screen_result_senders: RefCell<HashMap<WidgetId, Sender<Box<dyn Any + Send>>>>, pub pending_pop_result: RefCell<Option<Box<dyn Any + Send>>>,
}
Expand description

Shared application state passed by reference to every widget callback.

Provides access to the widget arena, CSS computed styles, focus state, screen stack, event/message queues, and service methods (push_screen, post_message, run_worker, toast).

Fields§

§arena: DenseSlotMap<WidgetId, Box<dyn Widget>>

Widget arena — all mounted widgets stored by their WidgetId.

§children: SecondaryMap<WidgetId, Vec<WidgetId>>

Parent-to-children mapping for the widget tree.

§parent: SecondaryMap<WidgetId, Option<WidgetId>>

Child-to-parent mapping for the widget tree.

§computed_styles: SecondaryMap<WidgetId, ComputedStyle>

CSS-cascaded styles for each mounted widget.

§inline_styles: SecondaryMap<WidgetId, Vec<Declaration>>

Per-widget inline style declarations (set via Widget::inline_styles).

§dirty: SecondaryMap<WidgetId, bool>

Per-widget dirty flag; set when the widget needs re-render.

§pseudo_classes: SecondaryMap<WidgetId, PseudoClassSet>

CSS pseudo-class state (hover, focus, etc.) for each widget.

§focused_widget: Option<WidgetId>

Currently focused widget, or None if nothing has focus.

§hovered_widget: Option<WidgetId>

Currently hovered widget (under mouse cursor). Updated by MouseMove events.

§screen_stack: Vec<WidgetId>

Stack of active screen widget IDs. Top of stack is the active screen.

§focus_history: Vec<Option<WidgetId>>

Saved focus state for each screen push. Parallel to screen_stack. push_screen saves focused_widget here; pop_screen restores it.

§pending_mounts: Vec<WidgetId>

Widgets scheduled to receive on_mount on the next event loop tick.

§input_buffer: String

Temporary input buffer for demo purposes (Phase 3 replaces with proper reactive state).

§event_tx: Option<Sender<AppEvent>>

Event bus sender — widgets and reactive effects post events here.

§message_queue: RefCell<Vec<(WidgetId, Box<dyn Any>)>>

Message queue for widget-to-widget communication. Uses RefCell so widgets can post messages from &self (on_event/on_action) without &mut. Drained by the event loop after each event is processed.

§pending_screen_pushes: RefCell<Vec<Box<dyn Widget>>>

Deferred screen pushes from widgets. Widgets in on_action(&self) can use push_screen_deferred() to schedule a new screen push without needing &mut AppContext. The event loop drains this after each action.

§pending_screen_pops: Cell<usize>

Number of screens to pop, deferred from widgets. Widgets in on_action(&self) use pop_screen_deferred() to schedule a screen pop. The event loop drains this counter after each action cycle.

§theme: Theme

Active theme for CSS variable resolution (e.g., $primary, $accent-lighten-2). Defaults to default_dark_theme(). Set a custom theme to change all variable colors.

§stylesheets: Vec<Stylesheet>

User stylesheets — stored here so ad-hoc pane rendering can resolve styles.

§worker_tx: Option<Sender<(WidgetId, Box<dyn Any + Send>)>>

Dedicated channel for worker results. Set by App::run_async before the event loop starts. Workers send (WidgetId, Box<dyn Any + Send>) through this channel to the event loop.

§worker_handles: RefCell<SecondaryMap<WidgetId, Vec<AbortHandle>>>

Per-widget abort handles for active workers. Used for auto-cancellation on unmount.

§pending_recompose: RefCell<Vec<WidgetId>>

Widgets that need recomposition (e.g. TabbedContent after tab switch). Drained by the event loop after each event cycle.

§active_overlay: RefCell<Option<Box<dyn Widget>>>

Active floating overlay (context menu, etc.). Rendered last, on top of everything. Not part of the widget tree — painted directly to the frame buffer at absolute coords.

§pending_overlay_dismiss: Cell<bool>

Deferred overlay dismissal flag. Set by dismiss_overlay(), drained after event handling.

§terminal_caps: TerminalCaps

Detected terminal capabilities (color depth, unicode, mouse, title). Widgets can inspect this to degrade gracefully on limited terminals.

§skip_animations: bool

When true, animations snap to their target value instead of interpolating. Set by TestApp to ensure deterministic rendering in tests.

§mouse_capture_stack: MouseCaptureStack

Stack-based mouse capture state. Screens/widgets push/pop to temporarily enable or disable mouse capture without competing callers clobbering each other.

§pending_mouse_push: RefCell<Vec<bool>>

Deferred mouse capture pushes from widgets (drained by event loop).

§pending_mouse_pops: Cell<usize>

Deferred mouse capture pop count from widgets (drained by event loop).

§loading_widgets: RefCell<SecondaryMap<WidgetId, bool>>

Per-widget loading state. When a widget’s ID is present and true, render_widget_tree draws a spinner overlay on top of that widget. Manipulated via set_loading(). Uses SecondaryMap (same as computed_styles, dirty, etc.).

§spinner_tick: Cell<u8>

Global spinner tick counter. Incremented once per full_render_pass. All loading overlays and LoadingIndicator widgets use this for synchronized animation.

§toast_entries: RefCell<Vec<ToastEntry>>

Stacked toast notifications, rendered bottom-right. Max 5 visible.

§pending_screen_wait_pushes: RefCell<Vec<(Box<dyn Widget>, Sender<Box<dyn Any + Send>>)>>

Deferred push_screen_wait requests: each entry is (screen_box, oneshot_sender). Drained by process_deferred_screens; the sender is stored keyed by the new screen’s WidgetId.

§screen_result_senders: RefCell<HashMap<WidgetId, Sender<Box<dyn Any + Send>>>>

Maps screen WidgetId -> oneshot sender for typed result delivery. Populated when push_screen_wait processes a deferred push; consumed when pop_screen_with fires.

§pending_pop_result: RefCell<Option<Box<dyn Any + Send>>>

Single-slot typed result for the next pop_screen_with call. Set by pop_screen_with, consumed by process_deferred_screens when the pop fires.

Implementations§

Source§

impl AppContext

Source

pub fn new() -> Self

Create a new empty AppContext with default state and the dark theme.

Source

pub fn set_theme(&mut self, theme: Theme)

Set the active theme, replacing all CSS variable colors. After calling this, a full re-cascade should be triggered to apply new theme colors.

Source

pub fn request_recompose(&self, id: WidgetId)

Schedule a widget for recomposition on the next event loop tick. Used by widgets like TabbedContent when their compose() output changes.

Source

pub fn dismiss_overlay(&self)

Schedule the active overlay for dismissal. Actual removal happens after the current event handler returns (avoids RefCell borrow conflict).

Source

pub fn push_screen_deferred(&self, screen: Box<dyn Widget>)

Push a new screen onto the screen stack.

The current screen is kept in memory; the new screen receives keyboard focus immediately. When the new screen is later popped, focus returns to the widget that was focused before the push.

Call this from on_action (where only &self is available). The push is applied at the end of the current event cycle.

To present a modal dialog that blocks input to all screens beneath it, wrap your widget in crate::widget::screen::ModalScreen:

struct ConfirmDialog;
impl Widget for ConfirmDialog {
    fn widget_type_name(&self) -> &'static str { "ConfirmDialog" }
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}
    fn on_action(&self, action: &str, ctx: &AppContext) {
        if action == "confirm" || action == "cancel" {
            ctx.pop_screen_deferred();
        }
    }
}

struct MyScreen;
impl Widget for MyScreen {
    fn widget_type_name(&self) -> &'static str { "MyScreen" }
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}
    fn on_action(&self, action: &str, ctx: &AppContext) {
        if action == "open_dialog" {
            ctx.push_screen_deferred(Box::new(ModalScreen::new(Box::new(ConfirmDialog))));
        }
    }
}
Source

pub fn pop_screen_deferred(&self)

Pop the top screen from the stack and restore focus to the previous screen.

The popped screen and its entire widget subtree are unmounted. Focus returns to whichever widget was focused when the screen was pushed — or advances to the next focusable widget if that widget no longer exists.

Call this from on_action (where only &self is available). The pop is applied at the end of the current event cycle.

Calling pop_screen_deferred on the last remaining screen is a no-op.

§Example
struct Dialog;
impl Widget for Dialog {
    fn widget_type_name(&self) -> &'static str { "Dialog" }
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}
    fn on_action(&self, action: &str, ctx: &AppContext) {
        match action {
            "ok" | "cancel" | "close" => ctx.pop_screen_deferred(),
            _ => {}
        }
    }
}
Source

pub fn push_screen_wait( &self, screen: Box<dyn Widget>, ) -> Receiver<Box<dyn Any + Send>>

Push a modal screen and asynchronously await a typed result.

Returns a tokio::sync::oneshot::Receiver that resolves when the modal screen calls pop_screen_with. The caller downcasts the Box<dyn Any> to the expected type.

Because on_action is synchronous, the typical usage pattern is to capture the receiver in a worker:

let rx = ctx.push_screen_wait(Box::new(ModalScreen::new(Box::new(dialog))));
ctx.run_worker(self_id, async move {
    if let Ok(boxed) = rx.await {
        let confirmed: bool = *boxed.downcast::<bool>().unwrap();
        confirmed
    } else {
        false
    }
});
Source

pub fn pop_screen_with<T: Any + Send + 'static>(&self, value: T)

Pop the top screen and deliver a typed result to the awaiting push_screen_wait caller.

The value is boxed and stored; process_deferred_screens delivers it through the oneshot channel when the pop is processed. If the top screen was not pushed via push_screen_wait, the result is silently discarded and the pop still occurs normally.

Call this from on_action in a modal’s inner widget to dismiss and return a value.

// Inside a dialog widget's on_action:
fn on_action(&self, action: &str, ctx: &AppContext) {
    match action {
        "ok"     => ctx.pop_screen_with(true),
        "cancel" => ctx.pop_screen_with(false),
        _ => {}
    }
}
Source

pub fn post_message(&self, source: WidgetId, message: impl Any + 'static)

Post a typed message from a widget. It will be dispatched via bubbling in the next event loop iteration. Takes &self so this can be called from on_event or on_action without borrow conflict.

Source

pub fn notify(&self, source: WidgetId, message: impl Any + 'static)

Convenience alias: post a message that bubbles up from the source widget. Equivalent to post_message — provided for API symmetry with Python Textual’s notify().

Source

pub fn run_worker<T: Send + 'static>( &self, source_id: WidgetId, fut: impl Future<Output = T> + 'static, ) -> AbortHandle

Spawn an async worker tied to a widget. The worker runs on the Tokio LocalSet. On completion, the result is delivered as a WorkerResult<T> message to the source widget via the message queue. T must be Send + ’static.

Returns an AbortHandle for manual cancellation. Workers are also automatically cancelled when the owning widget is unmounted.

§Panics

Panics if called outside of App::run() (worker_tx not initialized).

Source

pub fn run_worker_with_progress<T, P>( &self, source_id: WidgetId, progress_fn: impl FnOnce(Sender<P>) -> Pin<Box<dyn Future<Output = T>>> + 'static, ) -> AbortHandle
where T: Send + 'static, P: Send + 'static,

Spawn an async worker with a progress channel. The worker receives a flume::Sender<P> for sending progress updates, and its final result is delivered as a WorkerResult<T> message. Progress updates are delivered as WorkerProgress<P> messages to the source widget.

§Example
ctx.run_worker_with_progress(my_id, |progress_tx| {
    Box::pin(async move {
        for i in 0..100 {
            let _ = progress_tx.send(i as f32 / 100.0);
            tokio::time::sleep(Duration::from_millis(50)).await;
        }
        "done"
    })
});
Source

pub fn push_mouse_capture(&self, enabled: bool)

Schedule a mouse capture push deferred to the next event loop tick. Use from on_action(&self, ...) or on_event(&self, ...) where only &self is available.

Source

pub fn pop_mouse_capture(&self)

Schedule a mouse capture pop deferred to the next event loop tick. Use from on_action(&self, ...) or on_event(&self, ...) where only &self is available.

Source

pub fn set_loading(&self, id: WidgetId, loading: bool)

Set or clear the loading overlay for a widget.

When loading is true, render_widget_tree will draw a spinner overlay on top of the widget’s area after calling its render() method. When loading is false, the overlay is removed.

This is the textual-rs equivalent of Python Textual’s widget.loading = True.

§Example
// In on_action or on_message:
ctx.set_loading(self.own_id.get().unwrap(), true);
// Start async work...
// In worker result handler:
ctx.set_loading(self.own_id.get().unwrap(), false);
Source

pub fn toast( &self, message: impl Into<String>, severity: ToastSeverity, timeout_ms: u64, )

Display a toast notification in the bottom-right corner.

severity controls the border color: Info=$primary, Warning=$warning, Error=$error. timeout_ms controls auto-dismiss: 0 = persistent (never dismissed automatically).

Maximum 5 toasts are shown simultaneously; adding a 6th drops the oldest.

Source

pub fn toast_info(&self, message: impl Into<String>)

Display an Info toast with default 3000ms timeout.

Source

pub fn quit(&self)

Request a clean application exit. The event loop will break after the current frame.

Source

pub fn cancel_workers(&self, widget_id: WidgetId)

Cancel all workers associated with a widget. Called automatically during unmount.

Source

pub fn text_style(&self, id: WidgetId) -> Style

Get the ratatui text style (fg + bg) for a widget from its computed CSS. Returns Style::default() if the widget has no computed style.

Trait Implementations§

Source§

impl Default for AppContext

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> StorageAccess<T> for T

Source§

fn as_borrowed(&self) -> &T

Borrows the value.
Source§

fn into_taken(self) -> T

Takes the value.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.