diff --git a/src/symbols.rs b/src/symbols.rs index 798108c..25c7cda 100644 --- a/src/symbols.rs +++ b/src/symbols.rs @@ -54,4 +54,5 @@ pub mod double { pub const VERTICAL_RIGHT: &str = "╠"; pub const HORIZONTAL_DOWN: &str = "╦"; pub const HORIZONTAL_UP: &str = "╩"; + pub const CROSS: &str = "╬"; } diff --git a/src/widgets/block.rs b/src/widgets/block.rs index 6ca0df9..341280a 100644 --- a/src/widgets/block.rs +++ b/src/widgets/block.rs @@ -29,8 +29,11 @@ pub struct Block<'a> { title_style: Style, /// Visible borders borders: Borders, - /// Border style + /// Border style (meaning colors) border_style: Style, + /// Border type (meaning one of single lines w/sharp corners, single lines w/rounded corners, + /// or double lines w/sharp corners) + border_type: BorderType, /// Widget style style: Style, } @@ -42,11 +45,19 @@ impl<'a> Default for Block<'a> { title_style: Default::default(), borders: Borders::NONE, border_style: Default::default(), + border_type: BorderType::Plain, style: Default::default(), } } } +#[derive(Debug, Copy, Clone)] +pub enum BorderType { + Plain, + Rounded, + Double, +} + impl<'a> Block<'a> { pub fn title(mut self, title: &'a str) -> Block<'a> { self.title = Some(title); @@ -73,360 +84,8 @@ impl<'a> Block<'a> { self } - pub fn rounded(self) -> RoundedBlock<'a> { - RoundedBlock::from(self) - } - - pub fn double_border(self) -> DoubleBlock<'a> { - DoubleBlock::from(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) { - for y in area.top()..area.bottom() { - buf.get_mut(area.left(), y) - .set_symbol(line::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(line::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(line::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(line::HORIZONTAL) - .set_style(self.border_style); - } - } - - // Corners - if self.borders.contains(Borders::LEFT | Borders::TOP) { - buf.get_mut(area.left(), area.top()) - .set_symbol(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(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(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(line::BOTTOM_RIGHT) - .set_style(self.border_style); - } - - if area.width > 2 { - 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, - ); - } - } - } -} - -#[derive(Clone, Copy)] -pub struct RoundedBlock<'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, - /// Widget style - style: Style, -} - -impl<'a> From> for RoundedBlock<'a> { - fn from(block: Block<'a>) -> RoundedBlock<'a> { - RoundedBlock { - title: block.title, - title_style: block.title_style, - borders: block.borders, - border_style: block.border_style, - style: block.style, - } - } -} - -impl<'a> Default for RoundedBlock<'a> { - fn default() -> RoundedBlock<'a> { - RoundedBlock { - title: None, - title_style: Default::default(), - borders: Borders::NONE, - border_style: Default::default(), - style: Default::default(), - } - } -} - -impl<'a> RoundedBlock<'a> { - pub fn title(mut self, title: &'a str) -> RoundedBlock<'a> { - self.title = Some(title); - self - } - - pub fn title_style(mut self, style: Style) -> RoundedBlock<'a> { - self.title_style = style; - self - } - - pub fn border_style(mut self, style: Style) -> RoundedBlock<'a> { - self.border_style = style; - self - } - - pub fn style(mut self, style: Style) -> RoundedBlock<'a> { - self.style = style; - self - } - - pub fn borders(mut self, flag: Borders) -> RoundedBlock<'a> { - self.borders = flag; - 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 RoundedBlock<'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) { - for y in area.top()..area.bottom() { - buf.get_mut(area.left(), y) - .set_symbol(line::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(line::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(line::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(line::HORIZONTAL) - .set_style(self.border_style); - } - } - - // Corners - if self.borders.contains(Borders::LEFT | Borders::TOP) { - buf.get_mut(area.left(), area.top()) - .set_symbol(rounded::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(rounded::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(rounded::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(rounded::BOTTOM_RIGHT) - .set_style(self.border_style); - } - - if area.width > 2 { - 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, - ); - } - } - } -} - -#[derive(Clone, Copy)] -pub struct DoubleBlock<'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, - /// Widget style - style: Style, -} - -impl<'a> From> for DoubleBlock<'a> { - fn from(block: Block<'a>) -> DoubleBlock<'a> { - DoubleBlock { - title: block.title, - title_style: block.title_style, - borders: block.borders, - border_style: block.border_style, - style: block.style, - } - } -} -impl<'a> Default for DoubleBlock<'a> { - fn default() -> DoubleBlock<'a> { - DoubleBlock { - title: None, - title_style: Default::default(), - borders: Borders::NONE, - border_style: Default::default(), - style: Default::default(), - } - } -} - -impl<'a> DoubleBlock<'a> { - pub fn title(mut self, title: &'a str) -> DoubleBlock<'a> { - self.title = Some(title); - self - } - - pub fn title_style(mut self, style: Style) -> DoubleBlock<'a> { - self.title_style = style; - self - } - - pub fn border_style(mut self, style: Style) -> DoubleBlock<'a> { - self.border_style = style; - self - } - - pub fn style(mut self, style: Style) -> DoubleBlock<'a> { - self.style = style; - self - } - - pub fn borders(mut self, flag: Borders) -> DoubleBlock<'a> { - self.borders = flag; + pub fn set_border_type(mut self, border_type: BorderType) -> Block<'a> { + self.border_type = border_type; self } @@ -454,7 +113,7 @@ impl<'a> DoubleBlock<'a> { } } -impl<'a> Widget for DoubleBlock<'a> { +impl<'a> Widget for Block<'a> { fn draw(&mut self, area: Rect, buf: &mut Buffer) { if area.width < 2 || area.height < 2 { return; @@ -466,14 +125,24 @@ impl<'a> Widget for DoubleBlock<'a> { if self.borders.intersects(Borders::LEFT) { for y in area.top()..area.bottom() { buf.get_mut(area.left(), y) - .set_symbol(double::VERTICAL) + .set_symbol({ + match self.border_type { + BorderType::Double => double::VERTICAL, + _ => line::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(double::HORIZONTAL) + .set_symbol({ + match self.border_type { + BorderType::Double => double::HORIZONTAL, + _ => line::HORIZONTAL, + } + }) .set_style(self.border_style); } } @@ -481,7 +150,12 @@ impl<'a> Widget for DoubleBlock<'a> { let x = area.right() - 1; for y in area.top()..area.bottom() { buf.get_mut(x, y) - .set_symbol(double::VERTICAL) + .set_symbol({ + match self.border_type { + BorderType::Double => double::VERTICAL, + _ => line::VERTICAL, + } + }) .set_style(self.border_style); } } @@ -489,7 +163,12 @@ impl<'a> Widget for DoubleBlock<'a> { let y = area.bottom() - 1; for x in area.left()..area.right() { buf.get_mut(x, y) - .set_symbol(double::HORIZONTAL) + .set_symbol({ + match self.border_type { + BorderType::Double => double::HORIZONTAL, + _ => line::HORIZONTAL, + } + }) .set_style(self.border_style); } } @@ -497,22 +176,46 @@ impl<'a> Widget for DoubleBlock<'a> { // Corners if self.borders.contains(Borders::LEFT | Borders::TOP) { buf.get_mut(area.left(), area.top()) - .set_symbol(double::TOP_LEFT) + .set_symbol({ + match self.border_type { + BorderType::Double => double::TOP_LEFT, + BorderType::Rounded => 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(double::TOP_RIGHT) + .set_symbol({ + match self.border_type { + BorderType::Double => double::TOP_RIGHT, + BorderType::Rounded => 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(double::BOTTOM_LEFT) + .set_symbol({ + match self.border_type { + BorderType::Double => double::BOTTOM_LEFT, + BorderType::Rounded => 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(double::BOTTOM_RIGHT) + .set_symbol({ + match self.border_type { + BorderType::Double => double::BOTTOM_RIGHT, + BorderType::Rounded => rounded::BOTTOM_RIGHT, + _ => line::BOTTOM_RIGHT, + } + }) .set_style(self.border_style); }