1use crate::component::{Component, EventCx, MeasureCx};
2use crate::event::Event;
3use crate::geom::{Rect, Size};
4use crate::layout::Constraint;
5use crate::node::Node;
6use crate::render::RenderCx;
7use crate::style::{Border, Color, Style};
8
9pub struct Dialog {
15 inner: Node,
16 cancelled: bool,
17 confirmed: bool,
18 rect: Rect,
19 style: Style,
20 border: Border,
21}
22
23impl Dialog {
24 pub fn new(child: impl Component + 'static) -> Self {
26 Self {
27 inner: Node::new(child),
28 cancelled: false,
29 confirmed: false,
30 rect: Rect::default(),
31 style: Style::default(),
32 border: Border::Rounded,
33 }
34 }
35
36 pub fn border(mut self, border: Border) -> Self {
38 self.border = border;
39 self
40 }
41
42 pub fn style(mut self, style: Style) -> Self {
44 self.style = style;
45 self
46 }
47
48 pub fn cancelled(&self) -> bool { self.cancelled }
50
51 pub fn confirmed(&self) -> bool { self.confirmed }
53
54 pub fn reset(&mut self, cx: &mut EventCx) {
56 self.cancelled = false;
57 self.confirmed = false;
58 cx.invalidate_paint();
59 }
60}
61
62impl Component for Dialog {
63 fn render(&self, cx: &mut RenderCx) {
64 let r = self.rect;
66
67 cx.buffer.draw_border(r, self.border, &cx.style);
69
70 let inner_rect = r.inner(crate::geom::Insets::all(2));
72 let saved = self.inner.rect();
73 self.inner.set_rect(inner_rect);
74 self.inner.render(cx.buffer, cx.focused_id);
75 self.inner.set_rect(saved);
76
77 let footer_y = r.y.saturating_add(r.height.saturating_sub(1));
79 let footer_style = Style::default().fg(Color::Gray);
80 cx.buffer.write_text(
81 crate::geom::Pos { x: r.x.saturating_add(2), y: footer_y },
82 r,
83 "Enter: confirm Esc: cancel",
84 &footer_style,
85 );
86 cx.cursor.x = r.x;
88 cx.cursor.y = r.y;
89 }
90
91 fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
92 let child_size = self.inner.measure(constraint);
93 Size {
94 width: child_size.width.saturating_add(4), height: child_size.height.saturating_add(4), }
97 }
98
99 fn event(&mut self, event: &Event, cx: &mut EventCx) {
100 match event {
101 Event::Focus | Event::Blur | Event::Tick => return,
102 _ => {}
103 }
104
105 if cx.phase() != crate::event::EventPhase::Capture {
107 if let Event::Key(key_event) = event {
108 match &key_event.key {
109 crate::event::Key::Esc => {
110 self.cancelled = true;
111 cx.invalidate_paint();
112 return;
113 }
114 crate::event::Key::Enter => {
115 self.confirmed = true;
116 cx.invalidate_paint();
117 return;
118 }
119 _ => {}
120 }
121 }
122 }
123
124 if cx.phase() == crate::event::EventPhase::Capture {
126 let mut child_cx = EventCx::with_task_sender(
127 &mut self.inner.dirty, cx.global_dirty, cx.quit,
128 cx.phase, cx.propagation_stopped, cx.task_sender.clone(),
129 );
130 self.inner.component.event(event, &mut child_cx);
131 }
132 }
133
134 fn layout(&mut self, rect: Rect, _cx: &mut crate::component::LayoutCx) {
135 self.rect = rect;
136 let inner = rect.inner(crate::geom::Insets::all(2));
137 self.inner.layout(inner);
138 }
139
140 fn for_each_child(&self, f: &mut dyn FnMut(&Node)) { f(&self.inner); }
141 fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) { f(&mut self.inner); }
142 fn focusable(&self) -> bool { true }
143 fn style(&self) -> Style { self.style.clone() }
144}