use crate::buffer::Buffer; use crate::layout::Rect; use crate::style::Style; use crate::symbols::line; use crate::widgets::{Borders, Widget}; #[derive(Debug, Copy, Clone)] pub enum BorderType { Plain, Rounded, Double, } /// Base widget to be used with all upper level ones. It may be used to display a box border around /// the widget and/or add a title. /// /// # Examples /// /// ``` /// # use tui::widgets::{Block, BorderType, Borders}; /// # use tui::style::{Style, Color}; /// Block::default() /// .title("Block") /// .title_style(Style::default().fg(Color::Red)) /// .borders(Borders::LEFT | Borders::RIGHT) /// .border_style(Style::default().fg(Color::White)) /// .border_type(BorderType::Rounded) /// .style(Style::default().bg(Color::Black)); /// ``` #[derive(Clone, Copy)] pub struct Block<'a> { /// Optional title place on the upper left of the block title: Option<&'a str>, /// Title style title_style: Style, /// Visible borders borders: Borders, /// Border style border_style: Style, /// Type of the border. The default is plain lines but one can choose to have rounded corners /// or doubled lines instead. border_type: BorderType, /// Widget style style: Style, } impl<'a> Default for Block<'a> { fn default() -> Block<'a> { Block { title: None, title_style: Default::default(), borders: Borders::NONE, border_style: Default::default(), border_type: BorderType::Plain, style: Default::default(), } } } impl<'a> Block<'a> { pub fn title(mut self, title: &'a str) -> Block<'a> { self.title = Some(title); self } pub fn title_style(mut self, style: Style) -> Block<'a> { self.title_style = style; self } pub fn border_style(mut self, style: Style) -> Block<'a> { self.border_style = style; self } pub fn style(mut self, style: Style) -> Block<'a> { self.style = style; self } pub fn borders(mut self, flag: Borders) -> Block<'a> { self.borders = flag; self } pub fn border_type(mut self, border_type: BorderType) -> Block<'a> { self.border_type = border_type; self } /// Compute the inner area of a block based on its border visibility rules. pub fn inner(&self, area: Rect) -> Rect { if area.width < 2 || area.height < 2 { return Rect::default(); } let mut inner = area; if self.borders.intersects(Borders::LEFT) { inner.x += 1; inner.width -= 1; } if self.borders.intersects(Borders::TOP) || self.title.is_some() { inner.y += 1; inner.height -= 1; } if self.borders.intersects(Borders::RIGHT) { inner.width -= 1; } if self.borders.intersects(Borders::BOTTOM) { inner.height -= 1; } inner } } impl<'a> Widget for Block<'a> { fn draw(&mut self, area: Rect, buf: &mut Buffer) { if area.width < 2 || area.height < 2 { return; } self.background(area, buf, self.style.bg); // Sides if self.borders.intersects(Borders::LEFT) { let symbol = match self.border_type { BorderType::Double => line::DOUBLE_VERTICAL, _ => line::VERTICAL, }; for y in area.top()..area.bottom() { buf.get_mut(area.left(), y) .set_symbol(symbol) .set_style(self.border_style); } } if self.borders.intersects(Borders::TOP) { let symbol = match self.border_type { BorderType::Double => line::DOUBLE_HORIZONTAL, _ => line::HORIZONTAL, }; for x in area.left()..area.right() { buf.get_mut(x, area.top()) .set_symbol(symbol) .set_style(self.border_style); } } if self.borders.intersects(Borders::RIGHT) { let x = area.right() - 1; let symbol = match self.border_type { BorderType::Double => line::DOUBLE_VERTICAL, _ => line::VERTICAL, }; for y in area.top()..area.bottom() { buf.get_mut(x, y) .set_symbol(symbol) .set_style(self.border_style); } } if self.borders.intersects(Borders::BOTTOM) { let y = area.bottom() - 1; let symbol = match self.border_type { BorderType::Double => line::DOUBLE_HORIZONTAL, _ => line::HORIZONTAL, }; for x in area.left()..area.right() { buf.get_mut(x, y) .set_symbol(symbol) .set_style(self.border_style); } } // Corners if self.borders.contains(Borders::LEFT | Borders::TOP) { buf.get_mut(area.left(), area.top()) .set_symbol({ match self.border_type { BorderType::Double => line::DOUBLE_TOP_LEFT, BorderType::Rounded => line::ROUNDED_TOP_LEFT, _ => line::TOP_LEFT, } }) .set_style(self.border_style); } if self.borders.contains(Borders::RIGHT | Borders::TOP) { buf.get_mut(area.right() - 1, area.top()) .set_symbol({ match self.border_type { BorderType::Double => line::DOUBLE_TOP_RIGHT, BorderType::Rounded => line::ROUNDED_TOP_RIGHT, _ => line::TOP_RIGHT, } }) .set_style(self.border_style); } if self.borders.contains(Borders::LEFT | Borders::BOTTOM) { buf.get_mut(area.left(), area.bottom() - 1) .set_symbol({ match self.border_type { BorderType::Double => line::DOUBLE_BOTTOM_LEFT, BorderType::Rounded => line::ROUNDED_BOTTOM_LEFT, _ => line::BOTTOM_LEFT, } }) .set_style(self.border_style); } if self.borders.contains(Borders::RIGHT | Borders::BOTTOM) { buf.get_mut(area.right() - 1, area.bottom() - 1) .set_symbol({ match self.border_type { BorderType::Double => line::DOUBLE_BOTTOM_RIGHT, BorderType::Rounded => line::ROUNDED_BOTTOM_RIGHT, _ => line::BOTTOM_RIGHT, } }) .set_style(self.border_style); } if let Some(title) = self.title { let lx = if self.borders.intersects(Borders::LEFT) { 1 } else { 0 }; let rx = if self.borders.intersects(Borders::RIGHT) { 1 } else { 0 }; let width = area.width - lx - rx; buf.set_stringn( area.left() + lx, area.top(), title, width as usize, self.title_style, ); } } }