use core::ops::Deref; use crate::ui::{ component::{Child, Component, Event, EventCtx, Image, Label, Never}, geometry::{Grid, Insets, Rect}, }; use super::{theme, Button}; pub enum DialogMsg { Content(T), Controls(U), } pub struct Dialog { content: Child, controls: Child, } impl Dialog where T: Component, U: Component, { pub fn new(content: T, controls: U) -> Self { Self { content: Child::new(content), controls: Child::new(controls), } } pub fn inner(&self) -> &T { self.content.inner() } } impl Component for Dialog where T: Component, U: Component, { type Msg = DialogMsg; fn place(&mut self, bounds: Rect) -> Rect { let layout = DialogLayout::middle(bounds); self.content.place(layout.content); self.controls.place(layout.controls); bounds } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { self.content .event(ctx, event) .map(Self::Msg::Content) .or_else(|| self.controls.event(ctx, event).map(Self::Msg::Controls)) } fn paint(&mut self) { self.content.paint(); self.controls.paint(); } fn bounds(&self, sink: &mut dyn FnMut(Rect)) { self.content.bounds(sink); self.controls.bounds(sink); } } pub struct DialogLayout { pub content: Rect, pub controls: Rect, } impl DialogLayout { pub fn middle(area: Rect) -> Self { let grid = Grid::new(area, 5, 1); Self { content: Rect::new( grid.row_col(0, 0).top_left(), grid.row_col(3, 0).bottom_right(), ), controls: grid.row_col(4, 0), } } } #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Dialog where T: crate::trace::Trace, U: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.open("Dialog"); t.field("content", &self.content); t.field("controls", &self.controls); t.close(); } } pub struct IconDialog { image: Child, title: Label, description: Option>, controls: Child, } impl IconDialog where T: Deref, U: Component, { pub fn new(icon: &'static [u8], title: T, controls: U) -> Self { Self { image: Child::new(Image::new(icon)), title: Label::centered(title, theme::label_warning()), description: None, controls: Child::new(controls), } } pub fn with_description(mut self, description: T) -> Self { self.description = Some(Label::centered(description, theme::label_warning_value())); self } pub const ICON_AREA_HEIGHT: i32 = 64; pub const DESCRIPTION_SPACE: i32 = 13; pub const VALUE_SPACE: i32 = 9; } impl Component for IconDialog where T: Deref, U: Component, { type Msg = DialogMsg; fn place(&mut self, bounds: Rect) -> Rect { let bounds = bounds.inset(theme::borders()); let (content, buttons) = bounds.split_bottom(Button::<&str>::HEIGHT); let (image, content) = content.split_top(Self::ICON_AREA_HEIGHT); let content = content.inset(Insets::top(Self::DESCRIPTION_SPACE)); let (title, content) = content.split_top(self.title.size().y); let content = content.inset(Insets::top(Self::VALUE_SPACE)); self.image.place(image); self.title.place(title); self.description.place(content); self.controls.place(buttons); bounds } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { self.title.event(ctx, event); self.description.event(ctx, event); self.controls.event(ctx, event).map(Self::Msg::Controls) } fn paint(&mut self) { self.image.paint(); self.title.paint(); self.description.paint(); self.controls.paint(); } fn bounds(&self, sink: &mut dyn FnMut(Rect)) { self.image.bounds(sink); self.title.bounds(sink); self.description.bounds(sink); self.controls.bounds(sink); } } #[cfg(feature = "ui_debug")] impl crate::trace::Trace for IconDialog where T: Deref, U: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.open("IconDialog"); t.field("title", &self.title); if let Some(ref description) = self.description { t.field("description", description); } t.field("controls", &self.controls); t.close(); } }