Added ability to set title alignment, added tests, modified blocks example to show the feature

pull/462/head
Oleksandr Litus 3 years ago
parent 4e76bfa2ca
commit 57bd6affc0

@ -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<dyn Error>> {
// 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<dyn Error>> {
.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))

@ -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<Spans<'a>>,
/// 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);
}
}
}

@ -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);
}
Loading…
Cancel
Save