use crate::component::{Component, EventCx, LayoutCx, 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::{Color, Style};
pub struct Overlay {
background: Option<Node>,
foreground: Option<Node>,
active: bool,
}
impl Overlay {
pub fn new(
background: impl Component + 'static,
foreground: impl Component + 'static,
) -> Self {
Self {
background: Some(Node::new(background)),
foreground: Some(Node::new(foreground)),
active: false,
}
}
pub fn show(&mut self) { self.active = true; }
pub fn hide(&mut self) { self.active = false; }
pub fn is_active(&self) -> bool { self.active }
}
impl Component for Overlay {
fn render(&self, cx: &mut RenderCx) {
if let Some(bg) = &self.background {
bg.render_with_clip(cx.buffer, cx.focused_id, None);
}
if self.active {
let viewport = cx.rect;
for y in viewport.y..viewport.y.saturating_add(viewport.height) {
for x in viewport.x..viewport.x.saturating_add(viewport.width) {
if let Some(cell) = cx.buffer.get_mut(x, y) {
cell.style.bg = Some(Color::Black);
if cell.style.fg.is_none() {
cell.style.fg = Some(Color::Gray);
}
}
}
}
if let Some(fg) = &self.foreground {
let fg_size = fg.measure(Constraint::loose(viewport.width.saturating_sub(4), viewport.height.saturating_sub(4)));
let fg_w = fg_size.width.min(viewport.width.saturating_sub(4));
let fg_h = fg_size.height.min(viewport.height.saturating_sub(4));
let fg_x = viewport.x.saturating_add(viewport.width.saturating_sub(fg_w) / 2);
let fg_y = viewport.y.saturating_add(viewport.height.saturating_sub(fg_h) / 2);
let fg_rect = Rect { x: fg_x, y: fg_y, width: fg_w + 4, height: fg_h + 4 };
for y in fg_rect.y..fg_rect.y.saturating_add(fg_rect.height) {
for x in fg_rect.x..fg_rect.x.saturating_add(fg_rect.width) {
if let Some(cell) = cx.buffer.get_mut(x, y) {
cell.style.bg = Some(Color::White);
cell.style.fg = Some(Color::Black);
}
}
}
let inner = fg_rect.inner(crate::geom::Insets::all(2));
let saved = fg.rect();
fg.set_rect(inner);
fg.render(cx.buffer, cx.focused_id);
fg.set_rect(saved);
}
}
}
fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
if let Some(bg) = &self.background {
bg.measure(constraint)
} else {
Size { width: constraint.max.width, height: constraint.max.height }
}
}
fn focusable(&self) -> bool { false }
fn event(&mut self, event: &Event, cx: &mut EventCx) {
if matches!(event, Event::Focus | Event::Blur | Event::Tick) { return; }
if self.active {
if let Event::Key(key) = event {
if key.key == crate::event::Key::Esc {
self.hide();
cx.invalidate_paint();
return;
}
}
if let Some(fg) = &mut self.foreground {
let mut child_cx = EventCx::new(
&mut fg.dirty, cx.global_dirty, cx.quit, cx.phase, cx.propagation_stopped,
);
child_cx.task_sender = cx.task_sender.clone();
fg.component.event(event, &mut child_cx);
}
} else {
if let Some(bg) = &mut self.background {
let mut child_cx = EventCx::new(
&mut bg.dirty, cx.global_dirty, cx.quit, cx.phase, cx.propagation_stopped,
);
child_cx.task_sender = cx.task_sender.clone();
bg.component.event(event, &mut child_cx);
}
}
}
fn layout(&mut self, rect: Rect, _cx: &mut LayoutCx) {
if let Some(bg) = &mut self.background {
bg.layout(rect);
}
if let Some(fg) = &mut self.foreground {
fg.layout(rect); }
}
fn for_each_child(&self, f: &mut dyn FnMut(&Node)) {
if self.active {
if let Some(fg) = &self.foreground { f(fg); }
} else {
if let Some(bg) = &self.background { f(bg); }
}
}
fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) {
if self.active {
if let Some(fg) = &mut self.foreground { f(fg); }
} else {
if let Some(bg) = &mut self.background { f(bg); }
}
}
fn style(&self) -> Style { Style::default() }
}