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: StringTemporary 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: ThemeActive 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: TerminalCapsDetected terminal capabilities (color depth, unicode, mouse, title). Widgets can inspect this to degrade gracefully on limited terminals.
skip_animations: boolWhen true, animations snap to their target value instead of interpolating. Set by TestApp to ensure deterministic rendering in tests.
mouse_capture_stack: MouseCaptureStackStack-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
impl AppContext
Sourcepub fn set_theme(&mut self, theme: Theme)
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.
Sourcepub fn request_recompose(&self, id: WidgetId)
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.
Sourcepub fn dismiss_overlay(&self)
pub fn dismiss_overlay(&self)
Schedule the active overlay for dismissal. Actual removal happens after the current event handler returns (avoids RefCell borrow conflict).
Sourcepub fn push_screen_deferred(&self, screen: Box<dyn Widget>)
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))));
}
}
}Sourcepub fn pop_screen_deferred(&self)
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(),
_ => {}
}
}
}Sourcepub fn push_screen_wait(
&self,
screen: Box<dyn Widget>,
) -> Receiver<Box<dyn Any + Send>>
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
}
});Sourcepub fn pop_screen_with<T: Any + Send + 'static>(&self, value: T)
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),
_ => {}
}
}Sourcepub fn post_message(&self, source: WidgetId, message: impl Any + 'static)
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.
Sourcepub fn notify(&self, source: WidgetId, message: impl Any + 'static)
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().
Sourcepub fn run_worker<T: Send + 'static>(
&self,
source_id: WidgetId,
fut: impl Future<Output = T> + 'static,
) -> AbortHandle
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).
Sourcepub 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
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
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"
})
});Sourcepub fn push_mouse_capture(&self, enabled: bool)
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.
Sourcepub fn pop_mouse_capture(&self)
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.
Sourcepub fn set_loading(&self, id: WidgetId, loading: bool)
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);Sourcepub fn toast(
&self,
message: impl Into<String>,
severity: ToastSeverity,
timeout_ms: u64,
)
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.
Sourcepub fn toast_info(&self, message: impl Into<String>)
pub fn toast_info(&self, message: impl Into<String>)
Display an Info toast with default 3000ms timeout.
Sourcepub fn quit(&self)
pub fn quit(&self)
Request a clean application exit. The event loop will break after the current frame.
Sourcepub fn cancel_workers(&self, widget_id: WidgetId)
pub fn cancel_workers(&self, widget_id: WidgetId)
Cancel all workers associated with a widget. Called automatically during unmount.
Sourcepub fn text_style(&self, id: WidgetId) -> Style
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§
Auto Trait Implementations§
impl !Freeze for AppContext
impl !RefUnwindSafe for AppContext
impl !Send for AppContext
impl !Sync for AppContext
impl Unpin for AppContext
impl UnsafeUnpin for AppContext
impl !UnwindSafe for AppContext
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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