use crate::component::{Component, EventCx, MeasureCx};
use crate::event::Event;
use crate::geom::{Rect, Size};
use crate::layout::Constraint;
use crate::node::Node;
use crate::render::RenderCx;
use crate::style::{Border, Color, Style};
pub struct Dialog {
inner: Node,
cancelled: bool,
confirmed: bool,
rect: Rect,
style: Style,
border: Border,
}
impl Dialog {
pub fn new(child: impl Component + 'static) -> Self {
Self {
inner: Node::new(child),
cancelled: false,
confirmed: false,
rect: Rect::default(),
style: Style::default(),
border: Border::Rounded,
}
}
pub fn border(mut self, border: Border) -> Self {
self.border = border;
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn cancelled(&self) -> bool { self.cancelled }
pub fn confirmed(&self) -> bool { self.confirmed }
pub fn reset(&mut self, cx: &mut EventCx) {
self.cancelled = false;
self.confirmed = false;
cx.invalidate_paint();
}
}
impl Component for Dialog {
fn render(&self, cx: &mut RenderCx) {
let r = self.rect;
cx.buffer.draw_border(r, self.border, &cx.style);
let inner_rect = r.inner(crate::geom::Insets::all(2));
let saved = self.inner.rect();
self.inner.set_rect(inner_rect);
self.inner.render(cx.buffer, cx.focused_id);
self.inner.set_rect(saved);
let footer_y = r.y.saturating_add(r.height.saturating_sub(1));
let footer_style = Style::default().fg(Color::Gray);
cx.buffer.write_text(
crate::geom::Pos { x: r.x.saturating_add(2), y: footer_y },
r,
"Enter: confirm Esc: cancel",
&footer_style,
);
cx.cursor.x = r.x;
cx.cursor.y = r.y;
}
fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
let child_size = self.inner.measure(constraint);
Size {
width: child_size.width.saturating_add(4), height: child_size.height.saturating_add(4), }
}
fn event(&mut self, event: &Event, cx: &mut EventCx) {
match event {
Event::Focus | Event::Blur | Event::Tick => return,
_ => {}
}
if cx.phase() != crate::event::EventPhase::Capture {
if let Event::Key(key_event) = event {
match &key_event.key {
crate::event::Key::Esc => {
self.cancelled = true;
cx.invalidate_paint();
return;
}
crate::event::Key::Enter => {
self.confirmed = true;
cx.invalidate_paint();
return;
}
_ => {}
}
}
}
if cx.phase() == crate::event::EventPhase::Capture {
let mut child_cx = EventCx::with_task_sender(
&mut self.inner.dirty, cx.global_dirty, cx.quit,
cx.phase, cx.propagation_stopped, cx.task_sender.clone(),
);
self.inner.component.event(event, &mut child_cx);
}
}
fn layout(&mut self, rect: Rect, _cx: &mut crate::component::LayoutCx) {
self.rect = rect;
let inner = rect.inner(crate::geom::Insets::all(2));
self.inner.layout(inner);
}
fn for_each_child(&self, f: &mut dyn FnMut(&Node)) { f(&self.inner); }
fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) { f(&mut self.inner); }
fn focusable(&self) -> bool { true }
fn style(&self) -> Style { self.style.clone() }
}