| use crate::{ |
| buffer::Buffer, |
| layout::Rect, |
| style::Style, |
| symbols::line, |
| text::{Span, Spans}, |
| widgets::{Borders, Widget}, |
| }; |
| |
| #[derive(Debug, Clone, Copy)] |
| pub enum BorderType { |
| Plain, |
| Rounded, |
| Double, |
| Thick, |
| } |
| |
| impl BorderType { |
| pub fn line_symbols(border_type: BorderType) -> line::Set { |
| match border_type { |
| BorderType::Plain => line::NORMAL, |
| BorderType::Rounded => line::ROUNDED, |
| BorderType::Double => line::DOUBLE, |
| BorderType::Thick => line::THICK, |
| } |
| } |
| } |
| |
| /// 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") |
| /// .borders(Borders::LEFT | Borders::RIGHT) |
| /// .border_style(Style::default().fg(Color::White)) |
| /// .border_type(BorderType::Rounded) |
| /// .style(Style::default().bg(Color::Black)); |
| /// ``` |
| #[derive(Debug, Clone)] |
| pub struct Block<'a> { |
| /// Optional title place on the upper left of the block |
| title: Option<Spans<'a>>, |
| /// 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, |
| borders: Borders::NONE, |
| border_style: Default::default(), |
| border_type: BorderType::Plain, |
| style: Default::default(), |
| } |
| } |
| } |
| |
| impl<'a> Block<'a> { |
| pub fn title<T>(mut self, title: T) -> Block<'a> |
| where |
| T: Into<Spans<'a>>, |
| { |
| self.title = Some(title.into()); |
| self |
| } |
| |
| #[deprecated( |
| since = "0.10.0", |
| note = "You should use styling capabilities of `text::Spans` given as argument of the `title` method to apply styling to the title." |
| )] |
| pub fn title_style(mut self, style: Style) -> Block<'a> { |
| if let Some(t) = self.title { |
| let title = String::from(t); |
| self.title = Some(Spans::from(Span::styled(title, 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 render(self, area: Rect, buf: &mut Buffer) { |
| buf.set_style(area, self.style); |
| |
| if area.width < 2 || area.height < 2 { |
| return; |
| } |
| |
| let symbols = BorderType::line_symbols(self.border_type); |
| // Sides |
| if self.borders.intersects(Borders::LEFT) { |
| for y in area.top()..area.bottom() { |
| buf.get_mut(area.left(), y) |
| .set_symbol(symbols.vertical) |
| .set_style(self.border_style); |
| } |
| } |
| if self.borders.intersects(Borders::TOP) { |
| for x in area.left()..area.right() { |
| buf.get_mut(x, area.top()) |
| .set_symbol(symbols.horizontal) |
| .set_style(self.border_style); |
| } |
| } |
| if self.borders.intersects(Borders::RIGHT) { |
| let x = area.right() - 1; |
| for y in area.top()..area.bottom() { |
| buf.get_mut(x, y) |
| .set_symbol(symbols.vertical) |
| .set_style(self.border_style); |
| } |
| } |
| if self.borders.intersects(Borders::BOTTOM) { |
| let y = area.bottom() - 1; |
| for x in area.left()..area.right() { |
| buf.get_mut(x, y) |
| .set_symbol(symbols.horizontal) |
| .set_style(self.border_style); |
| } |
| } |
| |
| // Corners |
| if self.borders.contains(Borders::LEFT | Borders::TOP) { |
| buf.get_mut(area.left(), area.top()) |
| .set_symbol(symbols.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(symbols.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(symbols.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(symbols.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_spans(area.left() + lx, area.top(), &title, width); |
| } |
| } |
| } |