pub struct Dialog<S, M> { /* private fields */ }Expand description
A modal dialog: a centered, bordered box with a title in
its top border, a main content area (a description
paragraph, or a custom content closure), and a
standard action row or a custom footer.
§Declaring one
A Dialog is an ordinary Component. Declare it with
DeclareCtx::modal: that puts it on its own layer, above everything
declared before it, and gives it the whole layer’s keyboard fallback. Tab
cycles inside it, and a key nothing inside handles is absorbed by the
layer.
Wire on_dismiss and the dialog itself becomes a
focus target of last resort, so the dismiss key still lands somewhere when
nothing inside is focused. A dialog with no on_dismiss is not itself a
focus target.
Opening and closing is the app’s. Keep the open dialogs in a
ModalState and bind it with
Ratcn::modals; that also handles saving and
restoring the focus the user had before the dialog opened.
§Children
Anything declared from the content or
footer callbacks becomes a child of the dialog, sharing
its focus, hover, theme, event routing, and layer. Those two callbacks are
area overrides, not separate scopes: their children and any
action buttons all live in one sibling namespace, so ids
must be unique across the three.
Only the painted box participates in pointer routing, so a non-modal dialog does not block controls outside it.
Standard actions need no manual measurement or placement:
use ratcn::{Button, Dialog};
let _dialog: Dialog<(), Msg> = Dialog::new()
.title("Delete item")
.description("This cannot be undone.")
.action("cancel", Button::new("Cancel").secondary().on_press(|| Msg::Cancel))
.action("save", Button::new("Save").on_press(|| Msg::Save));Implementations§
Source§impl<S: 'static, M: 'static> Dialog<S, M>
impl<S: 'static, M: 'static> Dialog<S, M>
Sourcepub fn new() -> Self
pub fn new() -> Self
Create an empty dialog. Its focus scope wraps
(TabWrap::Wrap) so Tab cycles among its interactive descendants.
Sourcepub fn title(self, title: impl Into<String>) -> Self
pub fn title(self, title: impl Into<String>) -> Self
The title shown in the dialog’s top border.
Sourcepub fn description(self, description: impl Into<String>) -> Self
pub fn description(self, description: impl Into<String>) -> Self
A description paragraph for the main content area. The box auto-sizes to
fit it. Ignored if a content closure is set.
Sourcepub const fn outer_width(self, width: u16) -> Self
pub const fn outer_width(self, width: u16) -> Self
Set the preferred outer width in terminal cells, including the border and padding. The width is clamped to the area supplied to the dialog.
Sourcepub const fn outer_height(self, height: u16) -> Self
pub const fn outer_height(self, height: u16) -> Self
Set the preferred outer height in terminal cells, including the border, padding, content, and footer. The height is clamped to the area supplied to the dialog and takes precedence over automatic content measurement.
Sourcepub fn content(
self,
height: u16,
f: impl FnOnce(&mut DeclareCtx<'_, S, M>) + 'static,
) -> Self
pub fn content( self, height: u16, f: impl FnOnce(&mut DeclareCtx<'_, S, M>) + 'static, ) -> Self
Fill the main content area yourself. It supersedes
description.
The callback gets an ordinary DeclareCtx whose
area is the content strip; paint into it and
declare children with DeclareCtx::component as usual. Those
children belong to the dialog’s scope, sharing one sibling namespace
with the footer’s children and any action ids.
Focusable children just work: the runtime discovers them as they
declare, so there is nothing to announce.
The dialog cannot measure an arbitrary closure, so height states the
content strip’s exact height in terminal rows.
The closure is FnOnce, so it may consume owned values, but it is stored
on the retained component and so must capture only 'static values.
Sourcepub fn action(
self,
id: impl Into<ChildId>,
component: impl MeasuredComponent<S, M> + 'static,
) -> Self
pub fn action( self, id: impl Into<ChildId>, component: impl MeasuredComponent<S, M> + 'static, ) -> Self
Add a measured component to the standard action row.
Actions are end-aligned with standard spacing. Insertion order is both
visual order (left to right) and focus traversal order. Use
footer instead when the row needs custom layout.
Action ids share the Dialog sibling namespace with custom content
children.
action accepts any component that implements MeasuredComponent,
the trait for components that can report the size they need — that is
what lets the action row lay them out. See the trait’s implementors for
the current set. Route footer content that is not one of them through
footer.
§Panics
Panics if a custom footer was already configured.
Lay out a height-row footer yourself. It supersedes the standard
action row.
Reach for this when the row needs something the standard layout does not
do — a checkbox on the left, a status message beside the buttons. The
callback follows the same rules as content: an
ordinary DeclareCtx over the footer strip, children in the dialog’s
sibling namespace, 'static captures.
§Panics
Panics if action was already called. A dialog has one
footer, standard or custom, not both.
Sourcepub const fn offset(self, offset: CellOffset) -> Self
pub const fn offset(self, offset: CellOffset) -> Self
How far the box sits from its centered position, in cells.
A dialog is centered by default; this shifts it. Pass the offset your app
currently stores, each frame. On its own this just moves the box — pair it
with on_offset_change to let the user drag
it.
Sourcepub fn on_offset_change(
self,
on_change: impl Fn(CellOffset) -> M + 'static,
) -> Self
pub fn on_offset_change( self, on_change: impl Fn(CellOffset) -> M + 'static, ) -> Self
Make the dialog draggable by its border, and say what to emit as it moves.
Fires on every step of the drag rather than only on release, so the box
follows the pointer live — which requires storing the offset and passing
it back through offset. The emitted value is clamped
to keep the box inside the area supplied to the dialog.
Sourcepub fn on_dismiss(self, on_dismiss: impl Fn() -> M + 'static) -> Self
pub fn on_dismiss(self, on_dismiss: impl Fn() -> M + 'static) -> Self
Make the dismiss key — Esc unless dismiss_key
says otherwise — dismiss the dialog, emitting the message build
returns (the app names the close action — typically the same one the
Cancel button emits). Without this the dialog emits no dismissal; when
declared as a modal, the runtime still absorbs the unhandled key instead
of routing it to the base UI.
Wiring this is also what makes the dialog itself a focus target: focus
prefers a focusable descendant (an action, a custom child) and falls
back to the dialog only when there is none, so the dismiss key still
has somewhere to land. A dialog without on_dismiss is never focused
itself.
Sourcepub fn dismiss_key(self, key: impl Into<KeyChord>) -> Self
pub fn dismiss_key(self, key: impl Into<KeyChord>) -> Self
Which key dismisses the dialog (default Esc).
Only meaningful together with on_dismiss, which
supplies the message to emit. Accepts anything that converts into a
KeyChord, so a bare char or KeyCode works, with
ctrl / alt for combinations:
use ratcn::{Dialog, runtime::KeyChord};
let _dialog: Dialog<(), Msg> = Dialog::new()
.on_dismiss(|| Msg::Close)
.dismiss_key(KeyChord::from('w').ctrl());Sourcepub const fn tab_wrap(self, wrap: TabWrap) -> Self
pub const fn tab_wrap(self, wrap: TabWrap) -> Self
Override the Tab wrap-around behavior (default TabWrap::Wrap).
Sourcepub fn style(self, style: impl Fn(&Theme) -> DialogStyle + 'static) -> Self
pub fn style(self, style: impl Fn(&Theme) -> DialogStyle + 'static) -> Self
Replace the theme-derived DialogStyle.
The closure receives the active theme on each declaration pass, so deriving the result from that argument follows runtime theme changes. Return a fixed style to keep the same colors under every theme.
use ratcn::{Dialog, DialogStyle};
let _dialog: Dialog<(), ()> = Dialog::new().style(|theme| {
let mut style = DialogStyle::from_theme(theme);
style.border = theme.accent;
style
});Trait Implementations§
Source§impl<S: 'static, M: 'static> Component<S, M> for Dialog<S, M>
impl<S: 'static, M: 'static> Component<S, M> for Dialog<S, M>
Source§fn declare(&mut self, ctx: &mut DeclareCtx<'_, S, M>)
fn declare(&mut self, ctx: &mut DeclareCtx<'_, S, M>)
handle_event will need to read
back. Read moreSource§fn paint(&mut self, ctx: &mut PaintCtx<'_, S>)
fn paint(&mut self, ctx: &mut PaintCtx<'_, S>)
ctx carries the paint surface, area, app state,
theme, and interaction state. Read moreSource§fn scope_options(&self) -> ScopeOptions
fn scope_options(&self) -> ScopeOptions
declare, so it cannot depend on paint.Source§fn interaction_area(&self, area: Rect) -> Rect
fn interaction_area(&self, area: Rect) -> Rect
Source§fn handle_event(
&mut self,
event: &Event,
_state: &S,
ctx: &mut EventCtx<'_>,
) -> EventResult<M>
fn handle_event( &mut self, event: &Event, _state: &S, ctx: &mut EventCtx<'_>, ) -> EventResult<M>
Auto Trait Implementations§
impl<S, M> !RefUnwindSafe for Dialog<S, M>
impl<S, M> !Send for Dialog<S, M>
impl<S, M> !Sync for Dialog<S, M>
impl<S, M> !UnwindSafe for Dialog<S, M>
impl<S, M> Freeze for Dialog<S, M>
impl<S, M> Unpin for Dialog<S, M>
impl<S, M> UnsafeUnpin for Dialog<S, M>where
DialogBody<S, M>: UnsafeUnpin,
DialogFooter<S, M>: UnsafeUnpin,
Option<Box<dyn Fn(CellOffset) -> M>>: UnsafeUnpin,
Option<Box<dyn Fn() -> M>>: UnsafeUnpin,
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