use crate::component::{Component, EventCx, LayoutCx, MeasureCx};
use crate::event::Event;
use crate::geom::{Insets, Pos, Rect, Size};
use crate::layout::Constraint;
use crate::node::Node;
use crate::render::RenderCx;
use crate::style::{Border, Style};
pub struct Block {
child: Option<Node>,
style: Style,
title: Option<String>,
}
impl Block {
pub fn new(child: impl Component + 'static) -> Self {
Self {
child: Some(Node::new(child)),
style: Style::default(),
title: None,
}
}
pub fn border(mut self, border: Border) -> Self {
self.style = self.style.border(border);
self
}
pub fn padding(mut self, value: u16) -> Self {
self.style = self.style.padding(value);
self
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
}
impl Component for Block {
fn render(&self, cx: &mut RenderCx) {
cx.draw_border(self.style.border);
if let Some(title) = &self.title {
let pos = Pos { x: cx.rect.x.saturating_add(2), y: cx.rect.y };
cx.buffer.write_text(pos, cx.rect, title, &cx.style);
}
if let Some(child) = &self.child {
child.render_with_parent(cx.buffer, cx.focused_id, cx.clip_rect, cx.wrap, cx.truncate, cx.align, Some(&cx.style));
}
}
fn for_each_child(&self, f: &mut dyn FnMut(&Node)) {
if let Some(child) = &self.child {
f(child);
}
}
fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) {
if let Some(child) = &mut self.child {
f(child);
}
}
fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
let pad = self.effective_padding();
let child_constraint = Constraint {
min: Size::default(),
max: Size {
width: constraint.max.width.saturating_sub(pad.left + pad.right),
height: constraint.max.height.saturating_sub(pad.top + pad.bottom),
},
};
let child_size = self
.child
.as_ref()
.map(|c| c.measure(child_constraint))
.unwrap_or_default();
Size {
width: child_size.width.saturating_add(pad.left + pad.right),
height: child_size.height.saturating_add(pad.top + pad.bottom),
}
}
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 let Some(child) = &mut self.child {
let mut child_cx = EventCx::with_task_sender(&mut child.dirty, cx.global_dirty, cx.quit, cx.phase, cx.propagation_stopped, cx.task_sender.clone());
child.component.event(event, &mut child_cx);
}
}
fn layout(&mut self, rect: Rect, _cx: &mut LayoutCx) {
let inner = rect.inner(self.effective_padding());
if let Some(child) = &mut self.child {
child.layout(inner);
}
}
fn style(&self) -> Style {
self.style.clone()
}
}
impl Block {
fn effective_padding(&self) -> Insets {
let border_width: u16 = match self.style.border {
Border::None => 0,
_ => 1,
};
Insets {
top: self.style.padding.top + border_width,
right: self.style.padding.right + border_width,
bottom: self.style.padding.bottom + border_width,
left: self.style.padding.left + border_width,
}
}
}