use crate::core::buffer::Buffer;
use crate::core::rect::Rect;
use crate::core::style::Style;
use crate::widget::Widget;
pub struct Overlay {
pub id: String,
pub area: Rect,
pub captures_focus: bool,
}
#[derive(Default)]
pub struct OverlayStack {
entries: Vec<Overlay>,
}
impl OverlayStack {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, overlay: Overlay) {
self.entries.push(overlay);
}
pub fn remove(&mut self, id: &str) -> bool {
let before = self.entries.len();
self.entries.retain(|o| o.id != id);
self.entries.len() < before
}
pub fn top(&self) -> Option<&Overlay> {
self.entries.last()
}
pub fn has_focus_capture(&self) -> bool {
self.entries.iter().rev().any(|o| o.captures_focus)
}
pub fn focus_capture_id(&self) -> Option<&str> {
self.entries
.iter()
.rev()
.find(|o| o.captures_focus)
.map(|o| o.id.as_str())
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn iter(&self) -> impl Iterator<Item = &Overlay> {
self.entries.iter()
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
pub struct ModalBox {
title: String,
style: Style,
border_style: Style,
width_percent: u16,
height_percent: u16,
}
impl ModalBox {
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
style: Style::default(),
border_style: Style::default(),
width_percent: 60,
height_percent: 40,
}
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn border_style(mut self, style: Style) -> Self {
self.border_style = style;
self
}
pub fn width_percent(mut self, pct: u16) -> Self {
self.width_percent = pct.min(100);
self
}
pub fn height_percent(mut self, pct: u16) -> Self {
self.height_percent = pct.min(100);
self
}
pub fn inner_area(&self, parent: Rect) -> Rect {
let w = (parent.width as u32 * self.width_percent as u32 / 100) as u16;
let h = (parent.height as u32 * self.height_percent as u32 / 100) as u16;
let x = parent.x + (parent.width.saturating_sub(w)) / 2;
let y = parent.y + (parent.height.saturating_sub(h)) / 2;
Rect::new(x + 1, y + 1, w.saturating_sub(2), h.saturating_sub(2))
}
}
impl Widget for ModalBox {
fn render(self, area: Rect, buf: &mut Buffer) {
let dim_style = Style::default().dim();
buf.set_style(area, dim_style);
let w = (area.width as u32 * self.width_percent as u32 / 100) as u16;
let h = (area.height as u32 * self.height_percent as u32 / 100) as u16;
let x = area.x + (area.width.saturating_sub(w)) / 2;
let y = area.y + (area.height.saturating_sub(h)) / 2;
let modal_area = Rect::new(x, y, w, h);
buf.fill(modal_area, " ", self.style);
if w >= 2 && h >= 2 {
for bx in x..x + w {
buf.set_string(bx, y, "─", self.border_style);
buf.set_string(bx, y + h - 1, "─", self.border_style);
}
for by in y..y + h {
buf.set_string(x, by, "│", self.border_style);
buf.set_string(x + w - 1, by, "│", self.border_style);
}
buf.set_string(x, y, "┌", self.border_style);
buf.set_string(x + w - 1, y, "┐", self.border_style);
buf.set_string(x, y + h - 1, "└", self.border_style);
buf.set_string(x + w - 1, y + h - 1, "┘", self.border_style);
if !self.title.is_empty() && w > 4 {
let max_title = (w - 4) as usize;
let title: String = self.title.chars().take(max_title).collect();
buf.set_string(x + 2, y, &title, self.border_style);
}
}
}
}