Skip to main content

Dialog

Struct Dialog 

Source
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>

Source

pub fn new() -> Self

Create an empty dialog. Its focus scope wraps (TabWrap::Wrap) so Tab cycles among its interactive descendants.

Source

pub fn title(self, title: impl Into<String>) -> Self

The title shown in the dialog’s top border.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn footer( self, height: u16, f: impl FnOnce(&mut DeclareCtx<'_, S, M>) + 'static, ) -> Self

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.

Source

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.

Source

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.

Source

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.

Source

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());
Source

pub const fn tab_wrap(self, wrap: TabWrap) -> Self

Override the Tab wrap-around behavior (default TabWrap::Wrap).

Source

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>

Source§

fn declare(&mut self, ctx: &mut DeclareCtx<'_, S, M>)

Declare the component: lay out its area, declare its descendants, and record whatever handle_event will need to read back. Read more
Source§

fn paint(&mut self, ctx: &mut PaintCtx<'_, S>)

Paint the component. ctx carries the paint surface, area, app state, theme, and interaction state. Read more
Source§

fn scope_options(&self) -> ScopeOptions

The scope this component opens around its descendants. Read once, before declare, so it cannot depend on paint.
Source§

fn interaction_area(&self, area: Rect) -> Rect

Return the area used for focus, hit-testing, and event routing. Read more
Source§

fn handle_event( &mut self, event: &Event, _state: &S, ctx: &mut EventCtx<'_>, ) -> EventResult<M>

Offer this component an event. Read more
Source§

fn prepare(&mut self, _state: &State)

Prepare this component from the state it is being declared with. Read more
Source§

fn reveal_in_viewport( &mut self, _target: Rect, _state: &State, _ctx: &mut EventCtx<'_>, )

Bring target into view inside this component’s viewport. Read more
Source§

impl<S: 'static, M: 'static> Debug for Dialog<S, M>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<S: 'static, M: 'static> Default for Dialog<S, M>

Source§

fn default() -> Self

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

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>
where DialogBody<S, M>: Freeze, DialogFooter<S, M>: Freeze, Option<Box<dyn Fn(CellOffset) -> M>>: Freeze, Option<Box<dyn Fn() -> M>>: Freeze,

§

impl<S, M> Unpin for Dialog<S, M>
where DialogBody<S, M>: Unpin, DialogFooter<S, M>: Unpin, Option<Box<dyn Fn(CellOffset) -> M>>: Unpin, Option<Box<dyn Fn() -> M>>: Unpin,

§

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> 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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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.