From 57bd6affc0fd43cba82f4f911a7c2d0d16cfe7ac Mon Sep 17 00:00:00 2001 From: Oleksandr Litus Date: Sun, 28 Mar 2021 00:04:47 -0400 Subject: [PATCH 1/5] Added ability to set title alignment, added tests, modified blocks example to show the feature --- examples/block.rs | 16 +- src/widgets/block.rs | 34 +++- tests/widgets_block.rs | 422 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 464 insertions(+), 8 deletions(-) diff --git a/examples/block.rs b/examples/block.rs index 4937d37d..2919dc6a 100644 --- a/examples/block.rs +++ b/examples/block.rs @@ -6,7 +6,7 @@ use std::{error::Error, io}; use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; use tui::{ backend::TermionBackend, - layout::{Constraint, Direction, Layout}, + layout::{Constraint, Direction, Layout, Alignment}, style::{Color, Modifier, Style}, text::Span, widgets::{Block, BorderType, Borders}, @@ -30,21 +30,27 @@ fn main() -> Result<(), Box> { // Just draw the block and the group on the same area and build the group // with at least a margin of 1 let size = f.size(); + + // Surounding block let block = Block::default() .borders(Borders::ALL) .title("Main block with round corners") .border_type(BorderType::Rounded); f.render_widget(block, size); + let chunks = Layout::default() .direction(Direction::Vertical) .margin(4) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(f.size()); + // Top two inner blocks let top_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(chunks[0]); + + // Top left inner block with green background let block = Block::default() .title(vec![ Span::styled("With", Style::default().fg(Color::Yellow)), @@ -53,21 +59,27 @@ fn main() -> Result<(), Box> { .style(Style::default().bg(Color::Green)); f.render_widget(block, top_chunks[0]); + // Top right inner block with styled title aligned to the right let block = Block::default().title(Span::styled( "Styled title", Style::default() .fg(Color::White) .bg(Color::Red) .add_modifier(Modifier::BOLD), - )); + )).title_alignment(Alignment::Right); f.render_widget(block, top_chunks[1]); + // Bottom two inner blocks let bottom_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(chunks[1]); + + // Bottom left block with all default borders let block = Block::default().title("With borders").borders(Borders::ALL); f.render_widget(block, bottom_chunks[0]); + + // Bottom right block with styled left and right border let block = Block::default() .title("With styled borders and doubled borders") .border_style(Style::default().fg(Color::Cyan)) diff --git a/src/widgets/block.rs b/src/widgets/block.rs index 70610134..029dabdc 100644 --- a/src/widgets/block.rs +++ b/src/widgets/block.rs @@ -1,6 +1,6 @@ use crate::{ buffer::Buffer, - layout::Rect, + layout::{Rect, Alignment}, style::Style, symbols::line, text::{Span, Spans}, @@ -45,6 +45,9 @@ impl BorderType { pub struct Block<'a> { /// Optional title place on the upper left of the block title: Option>, + /// Title alignment. The default is top left of the block, but one can choose to place + /// title in the top middle, or top right of the block + title_alignment: Alignment, /// Visible borders borders: Borders, /// Border style @@ -60,6 +63,7 @@ impl<'a> Default for Block<'a> { fn default() -> Block<'a> { Block { title: None, + title_alignment: Alignment::Left, borders: Borders::NONE, border_style: Default::default(), border_type: BorderType::Plain, @@ -89,6 +93,11 @@ impl<'a> Block<'a> { self } + pub fn title_alignment(mut self, alignment: Alignment) -> Block<'a> { + self.title_alignment = alignment; + self + } + pub fn border_style(mut self, style: Style) -> Block<'a> { self.border_style = style; self @@ -192,19 +201,34 @@ impl<'a> Widget for Block<'a> { .set_style(self.border_style); } + // Title if let Some(title) = self.title { - let lx = if self.borders.intersects(Borders::LEFT) { + + let left_border_dx = if self.borders.intersects(Borders::LEFT) { 1 } else { 0 }; - let rx = if self.borders.intersects(Borders::RIGHT) { + + let right_border_dx = if self.borders.intersects(Borders::RIGHT) { 1 } else { 0 }; - let width = area.width.saturating_sub(lx).saturating_sub(rx); - buf.set_spans(area.left() + lx, area.top(), &title, width); + + let title_area_width = area.width.saturating_sub(left_border_dx) + .saturating_sub(right_border_dx); + + let title_dx = match self.title_alignment { + Alignment::Left => 0, + Alignment::Center => (title_area_width - title.width() as u16) / 2, + Alignment::Right => (title_area_width - title.width() as u16) + }; + + let title_x = area.left() + left_border_dx + title_dx; + let title_y = area.top(); + + buf.set_spans(title_x, title_y, &title, title_area_width); } } } diff --git a/tests/widgets_block.rs b/tests/widgets_block.rs index d3111081..dff73f9d 100644 --- a/tests/widgets_block.rs +++ b/tests/widgets_block.rs @@ -1,7 +1,7 @@ use tui::{ backend::TestBackend, buffer::Buffer, - layout::Rect, + layout::{Rect, Alignment}, style::{Color, Style}, text::Span, widgets::{Block, Borders}, @@ -211,3 +211,423 @@ fn widgets_block_renders_on_small_areas() { Buffer::with_lines(vec!["┌Test─"]), ); } + +#[test] +fn widgets_block_renders_title_top_left_all_borders() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::ALL); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ┌Title───────────┐ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " └────────────────┘ ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_left_no_left_border() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " Title────────────┐ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " ─────────────────┘ ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_left_no_right_border() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ┌Title──────────── ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " └───────────────── ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_left_no_borders() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::NONE); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " Title ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_center_all_borders() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::ALL); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ┌─────Title──────┐ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " └────────────────┘ ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_center_no_left_border() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ──────Title──────┐ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " ─────────────────┘ ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_center_no_right_border() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ┌──────Title────── ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " └───────────────── ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_center_no_borders() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::NONE); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " Title ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_right_all_borders() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::ALL); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ┌───────────Title┐ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " │ │ ", + " └────────────────┘ ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_right_no_left_border() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ────────────Title┐ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " ─────────────────┘ ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_right_no_right_border() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " ┌────────────Title ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " │ ", + " └───────────────── ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_block_renders_title_top_right_no_borders() { + let backend = TestBackend::new(20, 10); + let mut terminal = Terminal::new(backend).unwrap(); + + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::NONE); + + let area = Rect { + x: 1, + y: 1, + width: 18, + height: 8, + }; + + terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + + let expected = Buffer::with_lines(vec![ + " ", + " Title ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + ]); + + terminal.backend().assert_buffer(&expected); +} \ No newline at end of file From 4416135ddbfb5d86358931bd84b70d3336bc1855 Mon Sep 17 00:00:00 2001 From: Oleksandr Litus Date: Sun, 28 Mar 2021 00:08:42 -0400 Subject: [PATCH 2/5] Added test for inner with title in block --- src/widgets/block.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/widgets/block.rs b/src/widgets/block.rs index 029dabdc..d1af663c 100644 --- a/src/widgets/block.rs +++ b/src/widgets/block.rs @@ -531,5 +531,39 @@ mod tests { height: 0, }, ); + assert_eq!( + Block::default() + .title("Test") + .title_alignment(Alignment::Center) + .inner(Rect { + x: 0, + y: 0, + width: 0, + height: 1, + }), + Rect { + x: 0, + y: 1, + width: 0, + height: 0, + }, + ); + assert_eq!( + Block::default() + .title("Test") + .title_alignment(Alignment::Right) + .inner(Rect { + x: 0, + y: 0, + width: 0, + height: 1, + }), + Rect { + x: 0, + y: 1, + width: 0, + height: 0, + }, + ); } } From b95894d2b9607075d6b2a5f770680359a60ccb2c Mon Sep 17 00:00:00 2001 From: Oleksandr Litus Date: Sun, 28 Mar 2021 00:09:37 -0400 Subject: [PATCH 3/5] Updated block example to show center alignment --- examples/block.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/block.rs b/examples/block.rs index 2919dc6a..1ac3a70c 100644 --- a/examples/block.rs +++ b/examples/block.rs @@ -35,6 +35,7 @@ fn main() -> Result<(), Box> { let block = Block::default() .borders(Borders::ALL) .title("Main block with round corners") + .title_alignment(Alignment::Center) .border_type(BorderType::Rounded); f.render_widget(block, size); From 31b97447b74f29a4756243590ac264afa8dfa775 Mon Sep 17 00:00:00 2001 From: Oleksandr Litus Date: Sun, 28 Mar 2021 00:36:36 -0400 Subject: [PATCH 4/5] Formatting fixed --- examples/block.rs | 22 +++--- src/widgets/block.rs | 47 ++++++------- tests/widgets_block.rs | 148 +++++++++++++++++++++++++++-------------- 3 files changed, 134 insertions(+), 83 deletions(-) diff --git a/examples/block.rs b/examples/block.rs index 1ac3a70c..26d00da0 100644 --- a/examples/block.rs +++ b/examples/block.rs @@ -6,7 +6,7 @@ use std::{error::Error, io}; use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; use tui::{ backend::TermionBackend, - layout::{Constraint, Direction, Layout, Alignment}, + layout::{Alignment, Constraint, Direction, Layout}, style::{Color, Modifier, Style}, text::Span, widgets::{Block, BorderType, Borders}, @@ -50,7 +50,7 @@ fn main() -> Result<(), Box> { .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(chunks[0]); - + // Top left inner block with green background let block = Block::default() .title(vec![ @@ -60,14 +60,16 @@ fn main() -> Result<(), Box> { .style(Style::default().bg(Color::Green)); f.render_widget(block, top_chunks[0]); - // Top right inner block with styled title aligned to the right - let block = Block::default().title(Span::styled( - "Styled title", - Style::default() - .fg(Color::White) - .bg(Color::Red) - .add_modifier(Modifier::BOLD), - )).title_alignment(Alignment::Right); + // Top right inner block with styled title aligned to the right + let block = Block::default() + .title(Span::styled( + "Styled title", + Style::default() + .fg(Color::White) + .bg(Color::Red) + .add_modifier(Modifier::BOLD), + )) + .title_alignment(Alignment::Right); f.render_widget(block, top_chunks[1]); // Bottom two inner blocks diff --git a/src/widgets/block.rs b/src/widgets/block.rs index d1af663c..b4ec43eb 100644 --- a/src/widgets/block.rs +++ b/src/widgets/block.rs @@ -1,6 +1,6 @@ use crate::{ buffer::Buffer, - layout::{Rect, Alignment}, + layout::{Alignment, Rect}, style::Style, symbols::line, text::{Span, Spans}, @@ -203,7 +203,6 @@ impl<'a> Widget for Block<'a> { // Title if let Some(title) = self.title { - let left_border_dx = if self.borders.intersects(Borders::LEFT) { 1 } else { @@ -216,18 +215,20 @@ impl<'a> Widget for Block<'a> { 0 }; - let title_area_width = area.width.saturating_sub(left_border_dx) - .saturating_sub(right_border_dx); + let title_area_width = area + .width + .saturating_sub(left_border_dx) + .saturating_sub(right_border_dx); let title_dx = match self.title_alignment { - Alignment::Left => 0, + Alignment::Left => 0, Alignment::Center => (title_area_width - title.width() as u16) / 2, - Alignment::Right => (title_area_width - title.width() as u16) + Alignment::Right => (title_area_width - title.width() as u16), }; let title_x = area.left() + left_border_dx + title_dx; let title_y = area.top(); - + buf.set_spans(title_x, title_y, &title, title_area_width); } } @@ -533,14 +534,14 @@ mod tests { ); assert_eq!( Block::default() - .title("Test") - .title_alignment(Alignment::Center) - .inner(Rect { - x: 0, - y: 0, - width: 0, - height: 1, - }), + .title("Test") + .title_alignment(Alignment::Center) + .inner(Rect { + x: 0, + y: 0, + width: 0, + height: 1, + }), Rect { x: 0, y: 1, @@ -550,14 +551,14 @@ mod tests { ); assert_eq!( Block::default() - .title("Test") - .title_alignment(Alignment::Right) - .inner(Rect { - x: 0, - y: 0, - width: 0, - height: 1, - }), + .title("Test") + .title_alignment(Alignment::Right) + .inner(Rect { + x: 0, + y: 0, + width: 0, + height: 1, + }), Rect { x: 0, y: 1, diff --git a/tests/widgets_block.rs b/tests/widgets_block.rs index dff73f9d..ed080484 100644 --- a/tests/widgets_block.rs +++ b/tests/widgets_block.rs @@ -1,7 +1,7 @@ use tui::{ backend::TestBackend, buffer::Buffer, - layout::{Rect, Alignment}, + layout::{Alignment, Rect}, style::{Color, Style}, text::Span, widgets::{Block, Borders}, @@ -218,9 +218,9 @@ fn widgets_block_renders_title_top_left_all_borders() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::ALL); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::ALL); let area = Rect { x: 1, @@ -229,7 +229,11 @@ fn widgets_block_renders_title_top_left_all_borders() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -253,9 +257,9 @@ fn widgets_block_renders_title_top_left_no_left_border() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); let area = Rect { x: 1, @@ -264,7 +268,11 @@ fn widgets_block_renders_title_top_left_no_left_border() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -288,9 +296,9 @@ fn widgets_block_renders_title_top_left_no_right_border() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); let area = Rect { x: 1, @@ -299,7 +307,11 @@ fn widgets_block_renders_title_top_left_no_right_border() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -323,9 +335,9 @@ fn widgets_block_renders_title_top_left_no_borders() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::NONE); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Left) + .borders(Borders::NONE); let area = Rect { x: 1, @@ -334,7 +346,11 @@ fn widgets_block_renders_title_top_left_no_borders() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -358,9 +374,9 @@ fn widgets_block_renders_title_top_center_all_borders() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::ALL); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::ALL); let area = Rect { x: 1, @@ -369,7 +385,11 @@ fn widgets_block_renders_title_top_center_all_borders() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -393,9 +413,9 @@ fn widgets_block_renders_title_top_center_no_left_border() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); let area = Rect { x: 1, @@ -404,7 +424,11 @@ fn widgets_block_renders_title_top_center_no_left_border() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -428,9 +452,9 @@ fn widgets_block_renders_title_top_center_no_right_border() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); let area = Rect { x: 1, @@ -439,7 +463,11 @@ fn widgets_block_renders_title_top_center_no_right_border() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -463,9 +491,9 @@ fn widgets_block_renders_title_top_center_no_borders() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::NONE); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Center) + .borders(Borders::NONE); let area = Rect { x: 1, @@ -474,7 +502,11 @@ fn widgets_block_renders_title_top_center_no_borders() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -498,9 +530,9 @@ fn widgets_block_renders_title_top_right_all_borders() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::ALL); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::ALL); let area = Rect { x: 1, @@ -509,7 +541,11 @@ fn widgets_block_renders_title_top_right_all_borders() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -533,9 +569,9 @@ fn widgets_block_renders_title_top_right_no_left_border() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); let area = Rect { x: 1, @@ -544,7 +580,11 @@ fn widgets_block_renders_title_top_right_no_left_border() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -568,9 +608,9 @@ fn widgets_block_renders_title_top_right_no_right_border() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); let area = Rect { x: 1, @@ -579,7 +619,11 @@ fn widgets_block_renders_title_top_right_no_right_border() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -603,9 +647,9 @@ fn widgets_block_renders_title_top_right_no_borders() { let mut terminal = Terminal::new(backend).unwrap(); let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::NONE); + .title(Span::styled("Title", Style::default())) + .title_alignment(Alignment::Right) + .borders(Borders::NONE); let area = Rect { x: 1, @@ -614,7 +658,11 @@ fn widgets_block_renders_title_top_right_no_borders() { height: 8, }; - terminal.draw(|f| { f.render_widget(block, area); }).unwrap(); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); let expected = Buffer::with_lines(vec![ " ", @@ -630,4 +678,4 @@ fn widgets_block_renders_title_top_right_no_borders() { ]); terminal.backend().assert_buffer(&expected); -} \ No newline at end of file +} From ce04c24221a7af92014619ebad0b363b351761a3 Mon Sep 17 00:00:00 2001 From: Oleksandr Litus Date: Wed, 5 May 2021 12:18:15 +0300 Subject: [PATCH 5/5] Updated tests to use lamdas and be more concise. Updated title alignmnet code to be more straightforward and have correct behavior when placing title in the center without left border --- src/widgets/block.rs | 11 +- tests/widgets_block.rs | 555 ++++++++--------------------------------- 2 files changed, 117 insertions(+), 449 deletions(-) diff --git a/src/widgets/block.rs b/src/widgets/block.rs index b4ec43eb..ae5b58a9 100644 --- a/src/widgets/block.rs +++ b/src/widgets/block.rs @@ -221,12 +221,15 @@ impl<'a> Widget for Block<'a> { .saturating_sub(right_border_dx); let title_dx = match self.title_alignment { - Alignment::Left => 0, - Alignment::Center => (title_area_width - title.width() as u16) / 2, - Alignment::Right => (title_area_width - title.width() as u16), + Alignment::Left => left_border_dx, + Alignment::Center => area.width.saturating_sub(title.width() as u16) / 2, + Alignment::Right => area + .width + .saturating_sub(title.width() as u16) + .saturating_sub(right_border_dx), }; - let title_x = area.left() + left_border_dx + title_dx; + let title_x = area.left() + title_dx; let title_y = area.top(); buf.set_spans(title_x, title_y, &title, title_area_width); diff --git a/tests/widgets_block.rs b/tests/widgets_block.rs index ed080484..06d59aa6 100644 --- a/tests/widgets_block.rs +++ b/tests/widgets_block.rs @@ -213,469 +213,134 @@ fn widgets_block_renders_on_small_areas() { } #[test] -fn widgets_block_renders_title_top_left_all_borders() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::ALL); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " ┌Title───────────┐ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " └────────────────┘ ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_left_no_left_border() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " Title────────────┐ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " ─────────────────┘ ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_left_no_right_border() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " ┌Title──────────── ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " └───────────────── ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_left_no_borders() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Left) - .borders(Borders::NONE); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " Title ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_center_all_borders() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::ALL); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " ┌─────Title──────┐ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " └────────────────┘ ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_center_no_left_border() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " ──────Title──────┐ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " ─────────────────┘ ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_center_no_right_border() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " ┌──────Title────── ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " └───────────────── ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_center_no_borders() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Center) - .borders(Borders::NONE); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " Title ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - ]); - - terminal.backend().assert_buffer(&expected); -} - -#[test] -fn widgets_block_renders_title_top_right_all_borders() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); - - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::ALL); - - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; - - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); - - let expected = Buffer::with_lines(vec![ - " ", - " ┌───────────Title┐ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " │ │ ", - " └────────────────┘ ", - " ", - ]); +fn widgets_block_title_alignment() { + let test_case = |alignment, borders, expected| { + let backend = TestBackend::new(15, 2); + let mut terminal = Terminal::new(backend).unwrap(); - terminal.backend().assert_buffer(&expected); -} + let block = Block::default() + .title(Span::styled("Title", Style::default())) + .title_alignment(alignment) + .borders(borders); -#[test] -fn widgets_block_renders_title_top_right_no_left_border() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); + let area = Rect { + x: 1, + y: 0, + width: 13, + height: 2, + }; - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::TOP | Borders::RIGHT | Borders::BOTTOM); + terminal + .draw(|f| { + f.render_widget(block, area); + }) + .unwrap(); - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, + terminal.backend().assert_buffer(&expected); }; - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); + // title top-left with all borders + test_case( + Alignment::Left, + Borders::ALL, + Buffer::with_lines(vec![" ┌Title──────┐ ", " └───────────┘ "]), + ); - let expected = Buffer::with_lines(vec![ - " ", - " ────────────Title┐ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " ─────────────────┘ ", - " ", - ]); + // title top-left without top border + test_case( + Alignment::Left, + Borders::LEFT | Borders::BOTTOM | Borders::RIGHT, + Buffer::with_lines(vec![" │Title │ ", " └───────────┘ "]), + ); - terminal.backend().assert_buffer(&expected); -} + // title top-left with no left border + test_case( + Alignment::Left, + Borders::TOP | Borders::RIGHT | Borders::BOTTOM, + Buffer::with_lines(vec![" Title───────┐ ", " ────────────┘ "]), + ); -#[test] -fn widgets_block_renders_title_top_right_no_right_border() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); + // title top-left without right border + test_case( + Alignment::Left, + Borders::LEFT | Borders::TOP | Borders::BOTTOM, + Buffer::with_lines(vec![" ┌Title─────── ", " └──────────── "]), + ); - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM); + // title top-left without borders + test_case( + Alignment::Left, + Borders::NONE, + Buffer::with_lines(vec![" Title ", " "]), + ); - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; + // title center with all borders + test_case( + Alignment::Center, + Borders::ALL, + Buffer::with_lines(vec![" ┌───Title───┐ ", " └───────────┘ "]), + ); - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); + // title center without top border + test_case( + Alignment::Center, + Borders::LEFT | Borders::BOTTOM | Borders::RIGHT, + Buffer::with_lines(vec![" │ Title │ ", " └───────────┘ "]), + ); - let expected = Buffer::with_lines(vec![ - " ", - " ┌────────────Title ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " │ ", - " └───────────────── ", - " ", - ]); + // title center with no left border + test_case( + Alignment::Center, + Borders::TOP | Borders::RIGHT | Borders::BOTTOM, + Buffer::with_lines(vec![" ────Title───┐ ", " ────────────┘ "]), + ); - terminal.backend().assert_buffer(&expected); -} + // title center without right border + test_case( + Alignment::Center, + Borders::LEFT | Borders::TOP | Borders::BOTTOM, + Buffer::with_lines(vec![" ┌───Title──── ", " └──────────── "]), + ); -#[test] -fn widgets_block_renders_title_top_right_no_borders() { - let backend = TestBackend::new(20, 10); - let mut terminal = Terminal::new(backend).unwrap(); + // title center without borders + test_case( + Alignment::Center, + Borders::NONE, + Buffer::with_lines(vec![" Title ", " "]), + ); - let block = Block::default() - .title(Span::styled("Title", Style::default())) - .title_alignment(Alignment::Right) - .borders(Borders::NONE); + // title top-right with all borders + test_case( + Alignment::Right, + Borders::ALL, + Buffer::with_lines(vec![" ┌──────Title┐ ", " └───────────┘ "]), + ); - let area = Rect { - x: 1, - y: 1, - width: 18, - height: 8, - }; + // title top-right without top border + test_case( + Alignment::Right, + Borders::LEFT | Borders::BOTTOM | Borders::RIGHT, + Buffer::with_lines(vec![" │ Title│ ", " └───────────┘ "]), + ); - terminal - .draw(|f| { - f.render_widget(block, area); - }) - .unwrap(); + // title top-right with no left border + test_case( + Alignment::Right, + Borders::TOP | Borders::RIGHT | Borders::BOTTOM, + Buffer::with_lines(vec![" ───────Title┐ ", " ────────────┘ "]), + ); - let expected = Buffer::with_lines(vec![ - " ", - " Title ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - ]); + // title top-right without right border + test_case( + Alignment::Right, + Borders::LEFT | Borders::TOP | Borders::BOTTOM, + Buffer::with_lines(vec![" ┌───────Title ", " └──────────── "]), + ); - terminal.backend().assert_buffer(&expected); + // title top-right without borders + test_case( + Alignment::Right, + Borders::NONE, + Buffer::with_lines(vec![" Title ", " "]), + ); }