//! Example for the `dialog` widget. //! //! Demonstrates the three shapes the widget covers: //! //! * **Modal confirm** — title + subtitle + "Cancel" / "Delete" //! actions. Backed by `cancel( Msg::Cancel )` so pressing `Esc` //! matches tapping the Cancel button. Pointer events outside the //! card are silently absorbed. //! * **Non-modal pick** — `modal( false ) + dismiss_on_scrim( … )`. A //! tap on the scrim outside the card emits the dismiss message; ESC //! still fires `cancel`. //! * **Custom body** — a body element (here, a slider) injected //! between the subtitle and the action row. use ltk::{ App, ButtonVariant, Element, button, column, dialog, row, slider, spacer, stack, text, }; #[ derive( Clone, Copy, PartialEq, Eq ) ] enum Open { None, Modal, NonModal, CustomBody, } #[ derive( Clone ) ] enum Msg { Open( Open ), Close, Confirm, PickAlpha, PickBeta, PickGamma, BrightnessChanged( f32 ), } struct DialogApp { open: Open, last_event: String, brightness: f32, } impl DialogApp { fn new() -> Self { Self { open: Open::None, last_event: "(no dialog interactions yet)".to_string(), brightness: 0.5, } } } impl App for DialogApp { type Message = Msg; fn view( &self ) -> Element { let palette = ltk::theme_palette(); let primary = palette.text_primary; let secondary = palette.text_secondary; let header = column::() .spacing( 8.0 ) .push( text( "ltk · dialog widget" ).size( 24.0 ).color( primary ) ) .push( text( "Open one of the dialogs below. ESC always fires the dialog's `cancel` message." ) .size( 14.0 ) .color( secondary ) .wrap( true ) ); let openers = row::() .spacing( 12.0 ) .push( button::( "Modal confirm" ) .variant( ButtonVariant::Primary ) .on_press( Msg::Open( Open::Modal ) ), ) .push( button::( "Non-modal pick" ) .variant( ButtonVariant::Secondary ) .on_press( Msg::Open( Open::NonModal ) ), ) .push( button::( "Custom body" ) .variant( ButtonVariant::Tertiary ) .on_press( Msg::Open( Open::CustomBody ) ), ); let event_label = text( format!( "Last event: {}", self.last_event ) ) .size( 13.0 ) .color( secondary ) .wrap( true ); let main = column::() .spacing( 24.0 ) .padding( 32.0 ) .push( header ) .push( openers ) .push( spacer() ) .push( event_label ); // Build the active dialog (if any) and stack it on top of the // main column. Stack draws children in order, so the dialog // always sits over the main UI. let overlay: Option> = match self.open { Open::None => None, Open::Modal => Some( modal_confirm() ), Open::NonModal => Some( non_modal_pick() ), Open::CustomBody => Some( custom_body_dialog( self.brightness ) ), }; match overlay { Some( o ) => { stack::() .push( main ) .push( o ) .into() } None => main.into(), } } fn update( &mut self, msg: Msg ) { match msg { Msg::Open( o ) => { self.open = o; self.last_event = "opened".into(); } Msg::Close => { self.open = Open::None; self.last_event = "cancelled (button or ESC or scrim)".into(); } Msg::Confirm => { self.open = Open::None; self.last_event = "confirmed".into(); } Msg::PickAlpha => { self.open = Open::None; self.last_event = "picked Alpha".into(); } Msg::PickBeta => { self.open = Open::None; self.last_event = "picked Beta".into(); } Msg::PickGamma => { self.open = Open::None; self.last_event = "picked Gamma".into(); } Msg::BrightnessChanged( v ) => { self.brightness = v; } } } } fn modal_confirm() -> Element { dialog::() .title( "Delete partition?" ) .subtitle( "This will permanently erase every file in /dev/sda2 and cannot be undone. Make sure you have a backup before continuing." ) .cancel( Msg::Close ) .action( button::( "Cancel" ) .variant( ButtonVariant::Tertiary ) .on_press( Msg::Close ), ) .action( button::( "Delete" ) .variant( ButtonVariant::Primary ) .on_press( Msg::Confirm ), ) .into() } fn non_modal_pick() -> Element { dialog::() .modal( false ) .title( "Pick a flavour" ) .subtitle( "Tap the dim background or press ESC to cancel." ) .cancel( Msg::Close ) .dismiss_on_scrim( Msg::Close ) .action( button::( "Alpha" ) .variant( ButtonVariant::Tertiary ) .on_press( Msg::PickAlpha ), ) .action( button::( "Beta" ) .variant( ButtonVariant::Tertiary ) .on_press( Msg::PickBeta ), ) .action( button::( "Gamma" ) .variant( ButtonVariant::Primary ) .on_press( Msg::PickGamma ), ) .into() } fn custom_body_dialog( brightness: f32 ) -> Element { let palette = ltk::theme_palette(); let secondary = palette.text_secondary; let body = column::() .spacing( 8.0 ) .push( text( format!( "Brightness: {:>3.0} %", brightness * 100.0 ) ) .size( 14.0 ) .color( secondary ) ) .push( slider::( brightness ) .on_change( |v| Msg::BrightnessChanged( v ) ) .accent_thumb( true ), ); dialog::() .title( "Display brightness" ) .subtitle( "Drag the slider, then accept or cancel." ) .cancel( Msg::Close ) .body( body ) .action( button::( "Cancel" ) .variant( ButtonVariant::Tertiary ) .on_press( Msg::Close ), ) .action( button::( "Accept" ) .variant( ButtonVariant::Primary ) .on_press( Msg::Confirm ), ) .into() } fn main() { ltk::run( DialogApp::new() ); }