Defaults and other little things

pull/454/head^2
Sebastian Walz 3 years ago
parent 8832281dcf
commit 056ed16a61
No known key found for this signature in database
GPG Key ID: 7BB421C684E821D8

@ -82,7 +82,7 @@ The library comes with the following list of widgets:
* [Canvas (with line, point cloud, map)](examples/canvas.rs)
* [Tabs](examples/tabs.rs)
Click on each item to see the source of the example. Run the examples with with
Click on each item to see the source of the example. Run the examples with with
cargo (e.g. to run the demo `cargo run --example demo`), and quit by pressing `q`.
You can run all examples by running `make run-examples`.
@ -109,6 +109,7 @@ You can run all examples by running `make run-examples`.
* [termchat](https://github.com/lemunozm/termchat)
* [taskwarrior-tui](https://github.com/kdheepak/taskwarrior-tui)
* [gping](https://github.com/orf/gping/)
* [Vector](https://vector.dev)
### Alternatives

@ -19,7 +19,7 @@ impl<'a> Default for Label<'a> {
}
impl<'a> Widget for Label<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
buf.set_string(area.left(), area.top(), self.text, Style::default());
}
}

@ -1,3 +1,5 @@
//! Stuff related to the different backends like crossterm, curses, rustbox, termion and a backend for testing purposes.
use std::io;
use crate::buffer::Cell;
@ -26,15 +28,31 @@ pub use self::curses::CursesBackend;
mod test;
pub use self::test::TestBackend;
/// Shared interface of all backends.
pub trait Backend {
/// Draw something on the terminal-screen.
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>;
/// Hide the cursors.
fn hide_cursor(&mut self) -> Result<(), io::Error>;
/// Show the cursors.
fn show_cursor(&mut self) -> Result<(), io::Error>;
/// Get current row and column of the cursor.
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error>;
/// Set the row and column of the cursor.
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error>;
/// Clear the terminal-screen.
fn clear(&mut self) -> Result<(), io::Error>;
/// Get a rectangle of the same size as the terminal-screen.
fn size(&self) -> Result<Rect, io::Error>;
/// Execute any remaining read- and write-operations.
fn flush(&mut self) -> Result<(), io::Error>;
}

@ -20,6 +20,7 @@ impl<W> TermionBackend<W>
where
W: Write,
{
/// Construct a new `TermionBackend` for `stdout`.
pub fn new(stdout: W) -> TermionBackend<W> {
TermionBackend { stdout }
}
@ -142,7 +143,7 @@ struct ModifierDiff {
}
impl fmt::Display for Fg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use termion::color::Color as TermionColor;
match self.0 {
Color::Reset => termion::color::Reset.write_fg(f),
@ -168,7 +169,7 @@ impl fmt::Display for Fg {
}
}
impl fmt::Display for Bg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use termion::color::Color as TermionColor;
match self.0 {
Color::Reset => termion::color::Reset.write_bg(f),
@ -195,7 +196,7 @@ impl fmt::Display for Bg {
}
impl fmt::Display for ModifierDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let remove = self.from - self.to;
if remove.contains(Modifier::REVERSED) {
write!(f, "{}", termion::style::NoInvert)?;

@ -17,29 +17,29 @@ pub struct Cell {
}
impl Cell {
pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
pub fn set_symbol(&mut self, symbol: &str) -> &mut Self {
self.symbol.clear();
self.symbol.push_str(symbol);
self
}
pub fn set_char(&mut self, ch: char) -> &mut Cell {
pub fn set_char(&mut self, ch: char) -> &mut Self {
self.symbol.clear();
self.symbol.push(ch);
self
}
pub fn set_fg(&mut self, color: Color) -> &mut Cell {
pub fn set_fg(&mut self, color: Color) -> &mut Self {
self.fg = color;
self
}
pub fn set_bg(&mut self, color: Color) -> &mut Cell {
pub fn set_bg(&mut self, color: Color) -> &mut Self {
self.bg = color;
self
}
pub fn set_style(&mut self, style: Style) -> &mut Cell {
pub fn set_style(&mut self, style: Style) -> &mut Self {
if let Some(c) = style.fg {
self.fg = c;
}
@ -78,6 +78,15 @@ impl Default for Cell {
}
}
impl Into<Cell> for char {
fn into(self) -> Cell {
Cell {
symbol: self.into(),
..Default::default()
}
}
}
/// A buffer that maps to the desired content of the terminal after the draw call
///
/// No widget in the library interacts directly with the terminal. Instead each of them is required
@ -105,7 +114,7 @@ impl Default for Cell {
/// buf.get_mut(5, 0).set_char('x');
/// assert_eq!(buf.get(5, 0).symbol, "x");
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Buffer {
/// The area represented by this buffer
pub area: Rect,
@ -114,15 +123,6 @@ pub struct Buffer {
pub content: Vec<Cell>,
}
impl Default for Buffer {
fn default() -> Buffer {
Buffer {
area: Default::default(),
content: Vec::new(),
}
}
}
impl Buffer {
/// Returns a Buffer with all cells set to the default one
pub fn empty(area: Rect) -> Buffer {

@ -20,6 +20,12 @@ pub enum Direction {
Vertical,
}
impl Default for Direction {
fn default() -> Self {
Self::Vertical
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Constraint {
// TODO: enforce range 0 - 100
@ -30,6 +36,12 @@ pub enum Constraint {
Min(u16),
}
impl Default for Constraint {
fn default() -> Self {
Self::Ratio(1, 4)
}
}
impl Constraint {
pub fn apply(&self, length: u16) -> u16 {
match *self {
@ -45,7 +57,7 @@ impl Constraint {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct Margin {
pub vertical: u16,
pub horizontal: u16,
@ -58,6 +70,12 @@ pub enum Alignment {
Right,
}
impl Default for Alignment {
fn default() -> Self {
Self::Left
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Layout {
direction: Direction,
@ -350,7 +368,7 @@ impl Element {
/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
pub y: u16,
@ -358,17 +376,6 @@ pub struct Rect {
pub height: u16,
}
impl Default for Rect {
fn default() -> Rect {
Rect {
x: 0,
y: 0,
width: 0,
height: 0,
}
}
}
impl Rect {
/// Creates a new rect, with width and height limited to keep the area under max u16.
/// If clipped, aspect ratio will be preserved.

@ -71,7 +71,7 @@
//! implement your own.
//!
//! Each widget follows a builder pattern API providing a default configuration along with methods
//! to customize them. The widget is then rendered using the [`Frame::render_widget`] which take
//! to customize them. The widget is then rendered using the [`Frame::render`] which take
//! your widget instance an area to draw to.
//!
//! The following example renders a block of the size of the terminal:
@ -90,10 +90,10 @@
//! let mut terminal = Terminal::new(backend)?;
//! terminal.draw(|f| {
//! let size = f.size();
//! let block = Block::default()
//! let mut block = Block::default()
//! .title("Block")
//! .borders(Borders::ALL);
//! f.render_widget(block, size);
//! f.render(&mut block, size);
//! })?;
//! Ok(())
//! }
@ -129,14 +129,14 @@
//! ].as_ref()
//! )
//! .split(f.size());
//! let block = Block::default()
//! let mut block = Block::default()
//! .title("Block")
//! .borders(Borders::ALL);
//! f.render_widget(block, chunks[0]);
//! let block = Block::default()
//! f.render(&mut block, chunks[0]);
//! let mut block = Block::default()
//! .title("Block 2")
//! .borders(Borders::ALL);
//! f.render_widget(block, chunks[1]);
//! f.render(&mut block, chunks[1]);
//! })?;
//! Ok(())
//! }
@ -147,6 +147,26 @@
//! you might need a blank space somewhere, try to pass an additional constraint and don't use the
//! corresponding area.
#![warn(
clippy::all,
clippy::cargo,
clippy::nursery,
clippy::pedantic,
clippy::restriction,
future_incompatible,
//missing_docs,
rust_2018_idioms,
warnings,
)]
#![deny(
clippy::pedantic,
clippy::float_cmp_const,
clippy::indexing_slicing,
clippy::integer_arithmetic,
clippy::unwrap_used
)]
#![forbid(bare_trait_objects, unsafe_code)]
pub mod backend;
pub mod buffer;
pub mod layout;

@ -26,6 +26,12 @@ pub enum Color {
Indexed(u8),
}
impl Default for Color {
fn default() -> Self {
Self::Reset
}
}
bitflags! {
/// Modifier changes the way a piece of text is displayed.
///

@ -1,3 +1,42 @@
macro_rules! const_set {
(
$Name:ident {
full: $Full:expr,
seven_eighths: $SevenEighths:expr,
three_quarters: $ThreeQuarters:expr,
five_eighths: $FiveEighths:expr,
half: $Half:expr,
three_eighths: $ThreeEighths:expr,
one_quarter: $OneQuarter:expr,
one_eighth: $OneEighth:expr,
empty: $Empty:expr,
}
) => {
pub const $Name: Set = Set([
$Empty,
$OneEighth,
$OneQuarter,
$ThreeEighths,
$Half,
$FiveEighths,
$ThreeQuarters,
$SevenEighths,
$Full,
]);
/*pub const $Name: Set = Set {
full: $Full,
seven_eighths: $SevenEighths,
three_quarters: $ThreeQuarters,
five_eighths: $FiveEighths,
half: $Half,
three_eighths: $ThreeEighths,
one_quarter: $OneQuarter,
one_eighth: $OneEighth,
empty: $Empty,
};*/
};
}
pub mod block {
pub const FULL: &str = "█";
pub const SEVEN_EIGHTHS: &str = "▉";
@ -7,21 +46,32 @@ pub mod block {
pub const THREE_EIGHTHS: &str = "▍";
pub const ONE_QUARTER: &str = "▎";
pub const ONE_EIGHTH: &str = "▏";
pub const EMPTY: &str = " ";
pub const MAP: [&str; 9] = [
EMPTY,
ONE_EIGHTH,
ONE_QUARTER,
THREE_EIGHTHS,
HALF,
FIVE_EIGHTHS,
THREE_QUARTERS,
SEVEN_EIGHTHS,
FULL,
];
#[derive(Debug, Clone)]
pub struct Set {
pub full: &'static str,
pub seven_eighths: &'static str,
pub three_quarters: &'static str,
pub five_eighths: &'static str,
pub half: &'static str,
pub three_eighths: &'static str,
pub one_quarter: &'static str,
pub one_eighth: &'static str,
pub empty: &'static str,
pub fn from_level(level: f64) -> &'static str {
let level = (level * 8.0).round() as usize;
if level < 9 {
MAP[level]
} else {
EMPTY
}
}
pub const THREE_LEVELS: Set = Set {
#[derive(Debug, Clone)]
pub struct Set([&'static str; 9]);
const_set!(THREE_LEVELS {
full: FULL,
seven_eighths: FULL,
three_quarters: HALF,
@ -29,11 +79,11 @@ pub mod block {
half: HALF,
three_eighths: HALF,
one_quarter: HALF,
one_eighth: " ",
empty: " ",
};
one_eighth: EMPTY,
empty: EMPTY,
});
pub const NINE_LEVELS: Set = Set {
const_set!(NINE_LEVELS {
full: FULL,
seven_eighths: SEVEN_EIGHTHS,
three_quarters: THREE_QUARTERS,
@ -42,8 +92,8 @@ pub mod block {
three_eighths: THREE_EIGHTHS,
one_quarter: ONE_QUARTER,
one_eighth: ONE_EIGHTH,
empty: " ",
};
empty: EMPTY,
});
}
pub mod bar {
@ -55,21 +105,28 @@ pub mod bar {
pub const THREE_EIGHTHS: &str = "▃";
pub const ONE_QUARTER: &str = "▂";
pub const ONE_EIGHTH: &str = "▁";
pub const EMPTY: &str = " ";
#[derive(Debug, Clone)]
pub struct Set {
pub full: &'static str,
pub seven_eighths: &'static str,
pub three_quarters: &'static str,
pub five_eighths: &'static str,
pub half: &'static str,
pub three_eighths: &'static str,
pub one_quarter: &'static str,
pub one_eighth: &'static str,
pub empty: &'static str,
pub struct Set([&'static str; 9]);
impl Set {
pub fn symbol(&self, level: usize) -> &'static str {
if level < 8 {
self.0[level]
} else {
self.0[8]
}
}
}
impl Default for Set {
fn default() -> Self {
NINE_LEVELS
}
}
pub const THREE_LEVELS: Set = Set {
const_set!(THREE_LEVELS {
full: FULL,
seven_eighths: FULL,
three_quarters: HALF,
@ -77,11 +134,11 @@ pub mod bar {
half: HALF,
three_eighths: HALF,
one_quarter: HALF,
one_eighth: " ",
empty: " ",
};
one_eighth: EMPTY,
empty: EMPTY,
});
pub const NINE_LEVELS: Set = Set {
const_set!(NINE_LEVELS {
full: FULL,
seven_eighths: SEVEN_EIGHTHS,
three_quarters: THREE_QUARTERS,
@ -90,8 +147,8 @@ pub mod bar {
three_eighths: THREE_EIGHTHS,
one_quarter: ONE_QUARTER,
one_eighth: ONE_EIGHTH,
empty: " ",
};
empty: EMPTY,
});
}
pub mod line {
@ -143,7 +200,7 @@ pub mod line {
pub const DOUBLE_CROSS: &str = "╬";
pub const THICK_CROSS: &str = "╋";
#[derive(Debug, Clone)]
#[derive(Clone, Copy, Debug)]
pub struct Set {
pub vertical: &'static str,
pub horizontal: &'static str,
@ -158,6 +215,12 @@ pub mod line {
pub cross: &'static str,
}
impl Default for Set {
fn default() -> Self {
NORMAL
}
}
pub const NORMAL: Set = Set {
vertical: VERTICAL,
horizontal: HORIZONTAL,
@ -231,3 +294,9 @@ pub enum Marker {
/// Up to 8 points per cell
Braille,
}
impl Default for Marker {
fn default() -> Self {
Self::Dot
}
}

@ -56,7 +56,7 @@ where
}
/// Represents a consistent terminal interface for rendering.
pub struct Frame<'a, B: 'a>
pub struct Frame<'a, B>
where
B: Backend,
{
@ -73,12 +73,71 @@ impl<'a, B> Frame<'a, B>
where
B: Backend,
{
/// Render a [`Widget`] to the current buffer using [`Widget::render`] without consuming it.
///
/// # Examples
///
/// ```rust,no_run
/// # use std::io;
/// # use tui::Terminal;
/// # use tui::backend::TermionBackend;
/// # use tui::layout::Rect;
/// # use tui::widgets::Block;
/// # let stdout = io::stdout();
/// # let backend = TermionBackend::new(stdout);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// let mut block = Block::default();
/// let area = Rect::new(0, 0, 5, 5);
/// let mut frame = terminal.get_frame();
/// frame.render(&mut block, area);
/// ```
pub fn render<W>(&mut self, widget: &mut W, area: Rect)
where
W: Widget,
{
widget.render(area, self.terminal.current_buffer_mut());
}
/// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`] without consuming it.
///
/// The last argument should be an instance of the [`StatefulWidget::State`] associated to the
/// given [`StatefulWidget`].
///
/// # Examples
///
/// ```rust,no_run
/// # use std::io;
/// # use tui::Terminal;
/// # use tui::backend::TermionBackend;
/// # use tui::layout::Rect;
/// # use tui::widgets::{List, ListItem, ListState};
/// # let stdout = io::stdout();
/// # let backend = TermionBackend::new(stdout);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// let mut state = ListState::default();
/// state.select(Some(1));
/// let items = vec![
/// ListItem::new("Item 1"),
/// ListItem::new("Item 2"),
/// ];
/// let mut list = List::new(items);
/// let area = Rect::new(0, 0, 5, 5);
/// let mut frame = terminal.get_frame();
/// frame.render_stateful(&mut list, area, &mut state);
/// ```
pub fn render_stateful<W>(&mut self, widget: &mut W, area: Rect, state: &mut W::State)
where
W: StatefulWidget,
{
widget.render(area, self.terminal.current_buffer_mut(), state);
}
/// Terminal size, guaranteed not to change when rendering.
pub fn size(&self) -> Rect {
self.terminal.viewport.area
}
/// Render a [`Widget`] to the current buffer using [`Widget::render`].
/// Consume and render a [`Widget`] to the current buffer using [`Widget::render`].
///
/// # Examples
///
@ -96,14 +155,14 @@ where
/// let mut frame = terminal.get_frame();
/// frame.render_widget(block, area);
/// ```
pub fn render_widget<W>(&mut self, widget: W, area: Rect)
pub fn render_widget<W>(&mut self, mut widget: W, area: Rect)
where
W: Widget,
{
widget.render(area, self.terminal.current_buffer_mut());
self.render(&mut widget, area);
}
/// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`].
/// Consume and render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`].
///
/// The last argument should be an instance of the [`StatefulWidget::State`] associated to the
/// given [`StatefulWidget`].
@ -130,11 +189,11 @@ where
/// let mut frame = terminal.get_frame();
/// frame.render_stateful_widget(list, area, &mut state);
/// ```
pub fn render_stateful_widget<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
pub fn render_stateful_widget<W>(&mut self, mut widget: W, area: Rect, state: &mut W::State)
where
W: StatefulWidget,
{
widget.render(area, self.terminal.current_buffer_mut(), state);
self.render_stateful(&mut widget, area, state);
}
/// After drawing this frame, make the cursor visible and put it at the specified (x, y)
@ -204,7 +263,7 @@ where
}
/// Get a Frame object which provides a consistent view into the terminal state for rendering.
pub fn get_frame(&mut self) -> Frame<B> {
pub fn get_frame(&mut self) -> Frame<'_, B> {
Frame {
terminal: self,
cursor_position: None,
@ -255,9 +314,9 @@ where
/// Synchronizes terminal size, calls the rendering closure, flushes the current internal state
/// and prepares for the next draw call.
pub fn draw<F>(&mut self, f: F) -> io::Result<CompletedFrame>
pub fn draw<F>(&mut self, f: F) -> io::Result<CompletedFrame<'_>>
where
F: FnOnce(&mut Frame<B>),
F: FnOnce(&mut Frame<'_, B>),
{
// Autoresize - otherwise we get glitches if shrinking or potential desync between widgets
// and the terminal (if growing), which may OOB.

@ -28,33 +28,34 @@ use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone)]
pub struct BarChart<'a> {
/// Block to wrap the widget in
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// The width of each bar
bar_width: u16,
pub bar_width: u16,
/// The gap between each bar
bar_gap: u16,
pub bar_gap: u16,
/// Set of symbols used to display the data
bar_set: symbols::bar::Set,
pub bar_set: symbols::bar::Set,
/// Style of the bars
bar_style: Style,
pub bar_style: Style,
/// Style of the values printed at the bottom of each bar
value_style: Style,
pub value_style: Style,
/// Style of the labels printed under each bar
label_style: Style,
pub label_style: Style,
/// Style for the widget
style: Style,
/// Slice of (label, value) pair to plot on the chart
pub style: Style,
/// Slice of (label, value) pair to plot on the chart.
/// Cannot be modified directly, only with `set_data()`.
data: &'a [(&'a str, u64)],
/// Value necessary for a bar to reach the maximum height (if no value is specified,
/// the maximum value in the data is taken as reference)
max: Option<u64>,
pub max: Option<u64>,
/// Values to display on the bar (computed when the data is passed to the widget)
values: Vec<String>,
pub values: Vec<String>,
}
impl<'a> Default for BarChart<'a> {
fn default() -> BarChart<'a> {
BarChart {
fn default() -> Self {
Self {
block: None,
max: None,
data: &[],
@ -71,67 +72,71 @@ impl<'a> Default for BarChart<'a> {
}
impl<'a> BarChart<'a> {
pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> {
pub fn data(mut self, data: &'a [(&'a str, u64)]) -> Self {
self.set_data(data);
self
}
pub fn set_data(&mut self, data: &'a [(&'a str, u64)]) {
self.data = data;
self.values = Vec::with_capacity(self.data.len());
for &(_, v) in self.data {
self.values.push(format!("{}", v));
}
self
}
pub fn block(mut self, block: Block<'a>) -> BarChart<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn max(mut self, max: u64) -> BarChart<'a> {
pub fn max(mut self, max: u64) -> Self {
self.max = Some(max);
self
}
pub fn bar_style(mut self, style: Style) -> BarChart<'a> {
pub fn bar_style(mut self, style: Style) -> Self {
self.bar_style = style;
self
}
pub fn bar_width(mut self, width: u16) -> BarChart<'a> {
pub fn bar_width(mut self, width: u16) -> Self {
self.bar_width = width;
self
}
pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> {
pub fn bar_gap(mut self, gap: u16) -> Self {
self.bar_gap = gap;
self
}
pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> BarChart<'a> {
pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> Self {
self.bar_set = bar_set;
self
}
pub fn value_style(mut self, style: Style) -> BarChart<'a> {
pub fn value_style(mut self, style: Style) -> Self {
self.value_style = style;
self
}
pub fn label_style(mut self, style: Style) -> BarChart<'a> {
pub fn label_style(mut self, style: Style) -> Self {
self.label_style = style;
self
}
pub fn style(mut self, style: Style) -> BarChart<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
}
impl<'a> Widget for BarChart<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
let chart_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -163,17 +168,7 @@ impl<'a> Widget for BarChart<'a> {
.collect::<Vec<(&str, u64)>>();
for j in (0..chart_area.height - 1).rev() {
for (i, d) in data.iter_mut().enumerate() {
let symbol = match d.1 {
0 => self.bar_set.empty,
1 => self.bar_set.one_eighth,
2 => self.bar_set.one_quarter,
3 => self.bar_set.three_eighths,
4 => self.bar_set.half,
5 => self.bar_set.five_eighths,
6 => self.bar_set.three_quarters,
7 => self.bar_set.seven_eighths,
_ => self.bar_set.full,
};
let symbol = self.bar_set.symbol(d.1 as usize);
for x in 0..self.bar_width {
buf.get_mut(

@ -26,6 +26,12 @@ impl BorderType {
}
}
impl Default for BorderType {
fn default() -> Self {
Self::Plain
}
}
/// 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.
///
@ -41,35 +47,24 @@ impl BorderType {
/// .border_type(BorderType::Rounded)
/// .style(Style::default().bg(Color::Black));
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Block<'a> {
/// Optional title place on the upper left of the block
/// Optional title place on the upper left of the block.
/// Cannot be modified directly, only with `retitle()` and `untitle()`.
title: Option<Spans<'a>>,
/// Visible borders
borders: Borders,
pub borders: Borders,
/// Border style
border_style: Style,
pub 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,
pub 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(),
}
}
pub style: Style,
}
impl<'a> Block<'a> {
pub fn title<T>(mut self, title: T) -> Block<'a>
pub fn title<T>(mut self, title: T) -> Self
where
T: Into<Spans<'a>>,
{
@ -77,11 +72,22 @@ impl<'a> Block<'a> {
self
}
pub fn retitle<T>(&mut self, title: T)
where
T: Into<Spans<'a>>,
{
self.title = Some(title.into());
}
pub fn untitle(&mut self) {
self.title = None;
}
#[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> {
pub fn title_style(mut self, style: Style) -> Self {
if let Some(t) = self.title {
let title = String::from(t);
self.title = Some(Spans::from(Span::styled(title, style)));
@ -89,22 +95,26 @@ impl<'a> Block<'a> {
self
}
pub fn border_style(mut self, style: Style) -> Block<'a> {
pub fn border_style(mut self, style: Style) -> Self {
self.border_style = style;
self
}
pub fn style(mut self, style: Style) -> Block<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn borders(mut self, flag: Borders) -> Block<'a> {
pub fn borders(mut self, flag: Borders) -> Self {
self.borders = flag;
self
}
pub fn border_type(mut self, border_type: BorderType) -> Block<'a> {
pub fn set_borders(&mut self, flag: Borders) {
self.borders = flag;
}
pub fn border_type(mut self, border_type: BorderType) -> Self {
self.border_type = border_type;
self
}
@ -131,7 +141,7 @@ impl<'a> Block<'a> {
}
impl<'a> Widget for Block<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
if area.area() == 0 {
return;
}
@ -192,7 +202,7 @@ impl<'a> Widget for Block<'a> {
.set_style(self.border_style);
}
if let Some(title) = self.title {
if let Some(title) = &self.title {
let lx = if self.borders.intersects(Borders::LEFT) {
1
} else {

@ -14,7 +14,7 @@ pub struct Line {
}
impl Shape for Line {
fn draw(&self, painter: &mut Painter) {
fn draw(&self, painter: &mut Painter<'_, '_>) {
let (x1, y1) = match painter.get_point(self.x1, self.y1) {
Some(c) => c,
None => return,
@ -56,7 +56,14 @@ impl Shape for Line {
}
}
fn draw_line_low(painter: &mut Painter, x1: usize, y1: usize, x2: usize, y2: usize, color: Color) {
fn draw_line_low(
painter: &mut Painter<'_, '_>,
x1: usize,
y1: usize,
x2: usize,
y2: usize,
color: Color,
) {
let dx = (x2 - x1) as isize;
let dy = (y2 as isize - y1 as isize).abs();
let mut d = 2 * dy - dx;
@ -75,7 +82,14 @@ fn draw_line_low(painter: &mut Painter, x1: usize, y1: usize, x2: usize, y2: usi
}
}
fn draw_line_high(painter: &mut Painter, x1: usize, y1: usize, x2: usize, y2: usize, color: Color) {
fn draw_line_high(
painter: &mut Painter<'_, '_>,
x1: usize,
y1: usize,
x2: usize,
y2: usize,
color: Color,
) {
let dx = (x2 as isize - x1 as isize).abs();
let dy = (y2 - y1) as isize;
let mut d = 2 * dx - dy;

@ -12,6 +12,12 @@ pub enum MapResolution {
High,
}
impl Default for MapResolution {
fn default() -> Self {
Self::Low
}
}
impl MapResolution {
fn data(self) -> &'static [(f64, f64)] {
match self {
@ -22,23 +28,14 @@ impl MapResolution {
}
/// Shape to draw a world map with the given resolution and color
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Map {
pub resolution: MapResolution,
pub color: Color,
}
impl Default for Map {
fn default() -> Map {
Map {
resolution: MapResolution::Low,
color: Color::Reset,
}
}
}
impl Shape for Map {
fn draw(&self, painter: &mut Painter) {
fn draw(&self, painter: &mut Painter<'_, '_>) {
for (x, y) in self.resolution.data() {
if let Some((x, y)) = painter.get_point(*x, *y) {
painter.paint(x, y, self.color);

@ -20,7 +20,7 @@ use std::fmt::Debug;
/// Interface for all shapes that may be drawn on a Canvas widget.
pub trait Shape {
fn draw(&self, painter: &mut Painter);
fn draw(&self, painter: &mut Painter<'_, '_>);
}
/// Label to draw some text on the canvas
@ -211,9 +211,10 @@ impl<'a, 'b> Painter<'a, 'b> {
if width == 0.0 || height == 0.0 {
return None;
}
let x = ((x - left) * self.resolution.0 / width) as usize;
let y = ((top - y) * self.resolution.1 / height) as usize;
Some((x, y))
Some((
((x - left) * self.resolution.0 / width) as usize,
((top - y) * self.resolution.1 / height) as usize,
))
}
/// Paint a point of the grid
@ -342,7 +343,7 @@ impl<'a> Context<'a> {
/// ```
pub struct Canvas<'a, F>
where
F: Fn(&mut Context),
F: Fn(&mut Context<'_>),
{
block: Option<Block<'a>>,
x_bounds: [f64; 2],
@ -354,7 +355,7 @@ where
impl<'a, F> Default for Canvas<'a, F>
where
F: Fn(&mut Context),
F: Fn(&mut Context<'_>),
{
fn default() -> Canvas<'a, F> {
Canvas {
@ -370,7 +371,7 @@ where
impl<'a, F> Canvas<'a, F>
where
F: Fn(&mut Context),
F: Fn(&mut Context<'_>),
{
pub fn block(mut self, block: Block<'a>) -> Canvas<'a, F> {
self.block = Some(block);
@ -421,11 +422,11 @@ where
impl<'a, F> Widget for Canvas<'a, F>
where
F: Fn(&mut Context),
F: Fn(&mut Context<'_>),
{
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
let canvas_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area

@ -11,7 +11,7 @@ pub struct Points<'a> {
}
impl<'a> Shape for Points<'a> {
fn draw(&self, painter: &mut Painter) {
fn draw(&self, painter: &mut Painter<'_, '_>) {
for (x, y) in self.coords {
if let Some((x, y)) = painter.get_point(*x, *y) {
painter.paint(x, y, self.color);

@ -14,7 +14,7 @@ pub struct Rectangle {
}
impl Shape for Rectangle {
fn draw(&self, painter: &mut Painter) {
fn draw(&self, painter: &mut Painter<'_, '_>) {
let lines: [Line; 4] = [
Line {
x1: self.x,

@ -13,31 +13,21 @@ use std::{borrow::Cow, cmp::max};
use unicode_width::UnicodeWidthStr;
/// An X or Y axis for the chart widget
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Axis<'a> {
/// Title displayed next to axis end
/// Title displayed next to axis end.
/// Cannot be modified directly, only with `retitle()` and `untitle()`.
title: Option<Spans<'a>>,
/// Bounds for the axis (all data points outside these limits will not be represented)
bounds: [f64; 2],
pub bounds: [f64; 2],
/// A list of labels to put to the left or below the axis
labels: Option<Vec<Span<'a>>>,
pub labels: Option<Vec<Span<'a>>>,
/// The style used to draw the axis itself
style: Style,
}
impl<'a> Default for Axis<'a> {
fn default() -> Axis<'a> {
Axis {
title: None,
bounds: [0.0, 0.0],
labels: None,
style: Default::default(),
}
}
pub style: Style,
}
impl<'a> Axis<'a> {
pub fn title<T>(mut self, title: T) -> Axis<'a>
pub fn title<T>(mut self, title: T) -> Self
where
T: Into<Spans<'a>>,
{
@ -45,11 +35,22 @@ impl<'a> Axis<'a> {
self
}
pub fn retitle<T>(&mut self, title: T)
where
T: Into<Spans<'a>>,
{
self.title = Some(title.into());
}
pub fn untitle(&mut self) {
self.title = None;
}
#[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) -> Axis<'a> {
pub fn title_style(mut self, style: Style) -> Self {
if let Some(t) = self.title {
let title = String::from(t);
self.title = Some(Spans::from(Span::styled(title, style)));
@ -57,17 +58,17 @@ impl<'a> Axis<'a> {
self
}
pub fn bounds(mut self, bounds: [f64; 2]) -> Axis<'a> {
pub fn bounds(mut self, bounds: [f64; 2]) -> Self {
self.bounds = bounds;
self
}
pub fn labels(mut self, labels: Vec<Span<'a>>) -> Axis<'a> {
pub fn labels(mut self, labels: Vec<Span<'a>>) -> Self {
self.labels = Some(labels);
self
}
pub fn style(mut self, style: Style) -> Axis<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
@ -82,35 +83,30 @@ pub enum GraphType {
Line,
}
impl Default for GraphType {
fn default() -> Self {
Self::Scatter
}
}
/// A group of data points
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Dataset<'a> {
/// Name of the dataset (used in the legend if shown)
/// Name of the dataset (used in the legend if shown).
/// Cannot be modified directly, only with `rename()`.
name: Cow<'a, str>,
/// A reference to the actual data
data: &'a [(f64, f64)],
pub data: &'a [(f64, f64)],
/// Symbol used for each points of this dataset
marker: symbols::Marker,
pub marker: symbols::Marker,
/// Determines graph type used for drawing points
graph_type: GraphType,
pub graph_type: GraphType,
/// Style used to plot this dataset
style: Style,
}
impl<'a> Default for Dataset<'a> {
fn default() -> Dataset<'a> {
Dataset {
name: Cow::from(""),
data: &[],
marker: symbols::Marker::Dot,
graph_type: GraphType::Scatter,
style: Style::default(),
}
}
pub style: Style,
}
impl<'a> Dataset<'a> {
pub fn name<S>(mut self, name: S) -> Dataset<'a>
pub fn name<S>(mut self, name: S) -> Self
where
S: Into<Cow<'a, str>>,
{
@ -118,22 +114,29 @@ impl<'a> Dataset<'a> {
self
}
pub fn data(mut self, data: &'a [(f64, f64)]) -> Dataset<'a> {
pub fn rename<S>(&mut self, name: S)
where
S: Into<Cow<'a, str>>,
{
self.name = name.into();
}
pub fn data(mut self, data: &'a [(f64, f64)]) -> Self {
self.data = data;
self
}
pub fn marker(mut self, marker: symbols::Marker) -> Dataset<'a> {
pub fn marker(mut self, marker: symbols::Marker) -> Self {
self.marker = marker;
self
}
pub fn graph_type(mut self, graph_type: GraphType) -> Dataset<'a> {
pub fn graph_type(mut self, graph_type: GraphType) -> Self {
self.graph_type = graph_type;
self
}
pub fn style(mut self, style: Style) -> Dataset<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
@ -141,7 +144,7 @@ impl<'a> Dataset<'a> {
/// A container that holds all the infos about where to display each elements of the chart (axis,
/// labels, legend, ...).
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq)]
struct ChartLayout {
/// Location of the title of the x axis
title_x: Option<(u16, u16)>,
@ -161,21 +164,6 @@ struct ChartLayout {
graph_area: Rect,
}
impl Default for ChartLayout {
fn default() -> ChartLayout {
ChartLayout {
title_x: None,
title_y: None,
label_x: None,
label_y: None,
axis_x: None,
axis_y: None,
legend_area: None,
graph_area: Rect::default(),
}
}
}
/// A widget to plot one or more dataset in a cartesian coordinate system
///
/// # Examples
@ -212,25 +200,25 @@ impl Default for ChartLayout {
/// .bounds([0.0, 10.0])
/// .labels(["0.0", "5.0", "10.0"].iter().cloned().map(Span::from).collect()));
/// ```
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Chart<'a> {
/// A block to display around the widget eventually
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// The horizontal axis
x_axis: Axis<'a>,
pub x_axis: Axis<'a>,
/// The vertical axis
y_axis: Axis<'a>,
pub y_axis: Axis<'a>,
/// A reference to the datasets
datasets: Vec<Dataset<'a>>,
pub datasets: Vec<Dataset<'a>>,
/// The widget base style
style: Style,
pub style: Style,
/// Constraints used to determine whether the legend should be shown or not
hidden_legend_constraints: (Constraint, Constraint),
pub hidden_legend_constraints: (Constraint, Constraint),
}
impl<'a> Chart<'a> {
pub fn new(datasets: Vec<Dataset<'a>>) -> Chart<'a> {
Chart {
pub fn new(datasets: Vec<Dataset<'a>>) -> Self {
Self {
block: None,
x_axis: Axis::default(),
y_axis: Axis::default(),
@ -240,22 +228,25 @@ impl<'a> Chart<'a> {
}
}
pub fn block(mut self, block: Block<'a>) -> Chart<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn style(mut self, style: Style) -> Chart<'a> {
/// Style the `Chart`.
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn x_axis(mut self, axis: Axis<'a>) -> Chart<'a> {
/// Set the abscissa of the `Chart`.
pub fn x_axis(mut self, axis: Axis<'a>) -> Self {
self.x_axis = axis;
self
}
pub fn y_axis(mut self, axis: Axis<'a>) -> Chart<'a> {
/// Set the ordinate of the `Chart`.
pub fn y_axis(mut self, axis: Axis<'a>) -> Self {
self.y_axis = axis;
self
}
@ -276,7 +267,7 @@ impl<'a> Chart<'a> {
/// let _chart: Chart = Chart::new(vec![])
/// .hidden_legend_constraints(constraints);
/// ```
pub fn hidden_legend_constraints(mut self, constraints: (Constraint, Constraint)) -> Chart<'a> {
pub fn hidden_legend_constraints(mut self, constraints: (Constraint, Constraint)) -> Self {
self.hidden_legend_constraints = constraints;
self
}
@ -365,7 +356,7 @@ impl<'a> Chart<'a> {
}
impl<'a> Widget for Chart<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
if area.area() == 0 {
return;
}
@ -376,7 +367,7 @@ impl<'a> Widget for Chart<'a> {
let original_style = buf.get(area.left(), area.top()).style();
let chart_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -391,30 +382,37 @@ impl<'a> Widget for Chart<'a> {
}
if let Some(y) = layout.label_x {
let labels = self.x_axis.labels.unwrap();
let total_width = labels.iter().map(Span::width).sum::<usize>() as u16;
let labels_len = labels.len() as u16;
if total_width < graph_area.width && labels_len > 1 {
for (i, label) in labels.iter().enumerate() {
buf.set_span(
graph_area.left() + i as u16 * (graph_area.width - 1) / (labels_len - 1)
- label.content.width() as u16,
y,
label,
label.width() as u16,
);
if let Some(labels) = &self.x_axis.labels {
let total_width = labels.iter().map(Span::width).sum::<usize>() as u16;
let labels_len = labels.len() as u16;
if total_width < graph_area.width && labels_len > 1 {
for (i, label) in labels.iter().enumerate() {
buf.set_span(
graph_area.left()
+ i as u16 * (graph_area.width - 1) / (labels_len - 1)
- label.content.width() as u16,
y,
label,
label.width() as u16,
);
}
}
} else {
panic!("x_axis_labels must be something!");
}
}
if let Some(x) = layout.label_y {
let labels = self.y_axis.labels.unwrap();
let labels_len = labels.len() as u16;
for (i, label) in labels.iter().enumerate() {
let dy = i as u16 * (graph_area.height - 1) / (labels_len - 1);
if dy < graph_area.bottom() {
buf.set_span(x, graph_area.bottom() - 1 - dy, label, label.width() as u16);
if let Some(labels) = &self.y_axis.labels {
let labels_len = labels.len() as u16;
for (i, label) in labels.iter().enumerate() {
let dy = i as u16 * (graph_area.height - 1) / (labels_len - 1);
if dy < graph_area.bottom() {
buf.set_span(x, graph_area.bottom() - 1 - dy, label, label.width() as u16);
}
}
} else {
panic!("y_axis_labels must be something!");
}
}
@ -484,7 +482,10 @@ impl<'a> Widget for Chart<'a> {
}
if let Some((x, y)) = layout.title_x {
let title = self.x_axis.title.unwrap();
// If title is Some(Span{content: Cow::Owned(…), style: …}),
// this allocates memory,
// otherwise this clone is only copy by value.
let title = self.x_axis.title.clone().unwrap();
let width = graph_area.right().saturating_sub(x);
buf.set_style(
Rect {
@ -499,7 +500,10 @@ impl<'a> Widget for Chart<'a> {
}
if let Some((x, y)) = layout.title_y {
let title = self.y_axis.title.unwrap();
// If title is Some(Span{content: Cow::Owned(…), style: …}),
// this allocates memory,
// otherwise this clone is only copy by value.
let title = self.y_axis.title.clone().unwrap();
let width = graph_area.right().saturating_sub(x);
buf.set_style(
Rect {

@ -12,9 +12,9 @@ use crate::widgets::Widget;
/// # use tui::Frame;
/// # use tui::backend::Backend;
/// fn draw_on_clear<B: Backend>(f: &mut Frame<B>, area: Rect) {
/// let block = Block::default().title("Block").borders(Borders::ALL);
/// f.render_widget(Clear, area); // <- this will clear/reset the area first
/// f.render_widget(block, area); // now render the block widget
/// let mut block = Block::default().title("Block").borders(Borders::ALL);
/// f.render(&mut Clear, area); // <- this will clear/reset the area first
/// f.render(&mut block, area); // now render the block widget
/// }
/// ```
///
@ -22,11 +22,11 @@ use crate::widgets::Widget;
///
/// For a more complete example how to utilize `Clear` to realize popups see
/// the example `examples/popup.rs`
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct Clear;
impl Widget for Clear {
fn render(self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
for x in area.left()..area.right() {
for y in area.top()..area.bottom() {
buf.get_mut(x, y).reset();

@ -19,55 +19,57 @@ use crate::{
/// .gauge_style(Style::default().fg(Color::White).bg(Color::Black).add_modifier(Modifier::ITALIC))
/// .percent(20);
/// ```
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Gauge<'a> {
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
ratio: f64,
/// Cannot be modified directly, only with `relabel()` and `unlabel()`.
label: Option<Span<'a>>,
use_unicode: bool,
style: Style,
gauge_style: Style,
}
impl<'a> Default for Gauge<'a> {
fn default() -> Gauge<'a> {
Gauge {
block: None,
ratio: 0.0,
label: None,
use_unicode: false,
style: Style::default(),
gauge_style: Style::default(),
}
}
pub use_unicode: bool,
pub style: Style,
pub gauge_style: Style,
}
impl<'a> Gauge<'a> {
pub fn block(mut self, block: Block<'a>) -> Gauge<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn percent(mut self, percent: u16) -> Gauge<'a> {
pub fn percent(mut self, percent: u16) -> Self {
self.set_percent(percent);
self
}
pub fn set_percent(&mut self, percent: u16) {
assert!(
percent <= 100,
"Percentage should be between 0 and 100 inclusively."
);
self.ratio = f64::from(percent) / 100.0;
}
/// Sets ratio ([0.0, 1.0]) directly.
pub fn ratio(mut self, ratio: f64) -> Self {
self.set_ratio(ratio);
self
}
/// Sets ratio ([0.0, 1.0]) directly.
pub fn ratio(mut self, ratio: f64) -> Gauge<'a> {
pub fn set_ratio(&mut self, ratio: f64) {
assert!(
ratio <= 1.0 && ratio >= 0.0,
"Ratio should be between 0 and 1 inclusively."
);
self.ratio = ratio;
self
}
pub fn label<T>(mut self, label: T) -> Gauge<'a>
/// Gets the ratio.
pub fn get_ratio(&self) -> f64 {
self.ratio
}
pub fn label<T>(mut self, label: T) -> Self
where
T: Into<Span<'a>>,
{
@ -75,27 +77,38 @@ impl<'a> Gauge<'a> {
self
}
pub fn style(mut self, style: Style) -> Gauge<'a> {
pub fn relabel<T>(&mut self, label: T)
where
T: Into<Span<'a>>,
{
self.label = Some(label.into());
}
pub fn unlabel(&mut self) {
self.label = None;
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn gauge_style(mut self, style: Style) -> Gauge<'a> {
pub fn gauge_style(mut self, style: Style) -> Self {
self.gauge_style = style;
self
}
pub fn use_unicode(mut self, unicode: bool) -> Gauge<'a> {
pub fn use_unicode(mut self, unicode: bool) -> Self {
self.use_unicode = unicode;
self
}
}
impl<'a> Widget for Gauge<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
let gauge_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -118,8 +131,12 @@ impl<'a> Widget for Gauge<'a> {
};
// Label
let ratio = self.ratio;
// If label is Some(Span{content: Cow::Owned(…), style: …}),
// this allocates memory,
// otherwise this clone is only copy by value.
let label = self
.label
.clone()
.unwrap_or_else(|| Span::from(format!("{}%", (ratio * 100.0).round())));
for y in gauge_area.top()..gauge_area.bottom() {
// Gauge
@ -155,18 +172,8 @@ impl<'a> Widget for Gauge<'a> {
}
fn get_unicode_block<'a>(frac: f64) -> &'a str {
match (frac * 8.0).round() as u16 {
//get how many eighths the fraction is closest to
1 => symbols::block::ONE_EIGHTH,
2 => symbols::block::ONE_QUARTER,
3 => symbols::block::THREE_EIGHTHS,
4 => symbols::block::HALF,
5 => symbols::block::FIVE_EIGHTHS,
6 => symbols::block::THREE_QUARTERS,
7 => symbols::block::SEVEN_EIGHTHS,
8 => symbols::block::FULL,
_ => " ",
}
//get how many eighths the fraction is closest to
symbols::block::from_level(frac)
}
/// A compact widget to display a task progress over a single line.
@ -183,26 +190,15 @@ fn get_unicode_block<'a>(frac: f64) -> &'a str {
/// .line_set(symbols::line::THICK)
/// .ratio(0.4);
/// ```
#[derive(Clone, Debug, Default)]
pub struct LineGauge<'a> {
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// Cannot be modified directly, only with `set_percent()` and `set_ration()`.
ratio: f64,
label: Option<Spans<'a>>,
line_set: symbols::line::Set,
style: Style,
gauge_style: Style,
}
impl<'a> Default for LineGauge<'a> {
fn default() -> Self {
Self {
block: None,
ratio: 0.0,
label: None,
style: Style::default(),
line_set: symbols::line::NORMAL,
gauge_style: Style::default(),
}
}
pub label: Option<Spans<'a>>,
pub line_set: symbols::line::Set,
pub style: Style,
pub gauge_style: Style,
}
impl<'a> LineGauge<'a> {
@ -211,13 +207,32 @@ impl<'a> LineGauge<'a> {
self
}
pub fn percent(mut self, percent: u16) -> Self {
self.set_percent(percent);
self
}
pub fn set_percent(&mut self, percent: u16) {
assert!(
percent <= 100,
"Percentage should be between 0 and 100 inclusively."
);
self.ratio = f64::from(percent) / 100.0;
}
/// Sets ratio ([0.0, 1.0]) directly.
pub fn ratio(mut self, ratio: f64) -> Self {
self.set_ratio(ratio);
self
}
/// Sets ratio ([0.0, 1.0]) directly.
pub fn set_ratio(&mut self, ratio: f64) {
assert!(
ratio <= 1.0 && ratio >= 0.0,
"Ratio should be between 0 and 1 inclusively."
);
self.ratio = ratio;
self
}
pub fn line_set(mut self, set: symbols::line::Set) -> Self {
@ -245,10 +260,10 @@ impl<'a> LineGauge<'a> {
}
impl<'a> Widget for LineGauge<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
let gauge_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -261,9 +276,13 @@ impl<'a> Widget for LineGauge<'a> {
}
let ratio = self.ratio;
// If label is Some(Span{content: Cow::Owned(…), style: …}),
// this allocates memory,
// otherwise this clone is only copy by value.
let label = self
.label
.unwrap_or_else(move || Spans::from(format!("{:.0}%", ratio * 100.0)));
.clone()
.unwrap_or_else(|| Spans::from(format!("{:.0}%", ratio * 100.0)));
let (col, row) = buf.set_spans(
gauge_area.left(),
gauge_area.top(),

@ -8,21 +8,12 @@ use crate::{
use std::iter::{self, Iterator};
use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct ListState {
offset: usize,
selected: Option<usize>,
}
impl Default for ListState {
fn default() -> ListState {
ListState {
offset: 0,
selected: None,
}
}
}
impl ListState {
pub fn selected(&self) -> Option<usize> {
self.selected
@ -36,24 +27,31 @@ impl ListState {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq)]
pub struct ListItem<'a> {
content: Text<'a>,
style: Style,
pub style: Style,
}
impl<'a> ListItem<'a> {
pub fn new<T>(content: T) -> ListItem<'a>
pub fn new<T>(content: T) -> Self
where
T: Into<Text<'a>>,
{
ListItem {
Self {
content: content.into(),
style: Style::default(),
}
}
pub fn style(mut self, style: Style) -> ListItem<'a> {
pub fn content<T>(&mut self, content: T)
where
T: Into<Text<'a>>,
{
self.content = content.into();
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
@ -79,19 +77,19 @@ impl<'a> ListItem<'a> {
/// ```
#[derive(Debug, Clone)]
pub struct List<'a> {
block: Option<Block<'a>>,
items: Vec<ListItem<'a>>,
pub block: Option<Block<'a>>,
pub items: Vec<ListItem<'a>>,
/// Style used as a base style for the widget
style: Style,
start_corner: Corner,
pub style: Style,
pub start_corner: Corner,
/// Style used to render selected item
highlight_style: Style,
pub highlight_style: Style,
/// Symbol in front of the selected item (Shift all items to the right)
highlight_symbol: Option<&'a str>,
pub highlight_symbol: Option<&'a str>,
}
impl<'a> List<'a> {
pub fn new<T>(items: T) -> List<'a>
pub fn new<T>(items: T) -> Self
where
T: Into<Vec<ListItem<'a>>>,
{
@ -105,27 +103,27 @@ impl<'a> List<'a> {
}
}
pub fn block(mut self, block: Block<'a>) -> List<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn style(mut self, style: Style) -> List<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn highlight_symbol(mut self, highlight_symbol: &'a str) -> List<'a> {
pub fn highlight_symbol(mut self, highlight_symbol: &'a str) -> Self {
self.highlight_symbol = Some(highlight_symbol);
self
}
pub fn highlight_style(mut self, style: Style) -> List<'a> {
pub fn highlight_style(mut self, style: Style) -> Self {
self.highlight_style = style;
self
}
pub fn start_corner(mut self, corner: Corner) -> List<'a> {
pub fn start_corner(mut self, corner: Corner) -> Self {
self.start_corner = corner;
self
}
@ -134,10 +132,10 @@ impl<'a> List<'a> {
impl<'a> StatefulWidget for List<'a> {
type State = ListState;
fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
fn render(&mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
buf.set_style(area, self.style);
let list_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -242,7 +240,7 @@ impl<'a> StatefulWidget for List<'a> {
}
impl<'a> Widget for List<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
let mut state = ListState::default();
StatefulWidget::render(self, area, buf, &mut state);
}

@ -24,6 +24,7 @@ mod gauge;
mod list;
mod paragraph;
mod reflow;
mod seven_segment;
mod sparkline;
mod table;
mod tabs;
@ -35,6 +36,7 @@ pub use self::clear::Clear;
pub use self::gauge::{Gauge, LineGauge};
pub use self::list::{List, ListItem, ListState};
pub use self::paragraph::{Paragraph, Wrap};
pub use self::seven_segment::SevenSegment;
pub use self::sparkline::Sparkline;
pub use self::table::{Cell, Row, Table, TableState};
pub use self::tabs::Tabs;
@ -46,17 +48,23 @@ bitflags! {
/// Bitflags that can be composed to set the visible borders essentially on the block widget.
pub struct Borders: u32 {
/// Show no border (default)
const NONE = 0b0000_0001;
const NONE = 0b0000_0001;
/// Show the top border
const TOP = 0b0000_0010;
const TOP = 0b0000_0010;
/// Show the right border
const RIGHT = 0b0000_0100;
const RIGHT = 0b0000_0100;
/// Show the bottom border
const BOTTOM = 0b000_1000;
const BOTTOM = 0b000_1000;
/// Show the left border
const LEFT = 0b0001_0000;
const LEFT = 0b0001_0000;
/// Show all borders
const ALL = Self::TOP.bits | Self::RIGHT.bits | Self::BOTTOM.bits | Self::LEFT.bits;
const ALL = Self::TOP.bits | Self::RIGHT.bits | Self::BOTTOM.bits | Self::LEFT.bits;
}
}
impl Default for Borders {
fn default() -> Self {
Borders::NONE
}
}
@ -64,7 +72,7 @@ bitflags! {
pub trait Widget {
/// Draws the current state of the widget in the given buffer. That the only method required to
/// implement a custom widget.
fn render(self, area: Rect, buf: &mut Buffer);
fn render(&mut self, area: Rect, buf: &mut Buffer);
}
/// A `StatefulWidget` is a widget that can take advantage of some local state to remember things
@ -116,7 +124,7 @@ pub trait Widget {
/// }
///
/// // Select the next item. This will not be reflected until the widget is drawn in the
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
/// // `Terminal::draw` callback using `Frame::render_stateful`.
/// pub fn next(&mut self) {
/// let i = match self.state.selected() {
/// Some(i) => {
@ -132,7 +140,7 @@ pub trait Widget {
/// }
///
/// // Select the previous item. This will not be reflected until the widget is drawn in the
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
/// // `Terminal::draw` callback using `Frame::render_stateful`.
/// pub fn previous(&mut self) {
/// let i = match self.state.selected() {
/// Some(i) => {
@ -169,10 +177,10 @@ pub trait Widget {
/// // that is understood by tui.
/// let items: Vec<ListItem>= events.items.iter().map(|i| ListItem::new(i.as_ref())).collect();
/// // The `List` widget is then built with those items.
/// let list = List::new(items);
/// let mut list = List::new(items);
/// // Finally the widget is rendered using the associated state. `events.state` is
/// // effectively the only thing that we will "remember" from this draw call.
/// f.render_stateful_widget(list, f.size(), &mut events.state);
/// f.render_stateful(&mut list, f.size(), &mut events.state);
/// });
///
/// // In response to some input events or an external http request or whatever:
@ -181,5 +189,5 @@ pub trait Widget {
/// ```
pub trait StatefulWidget {
type State;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
fn render(&mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
}

@ -42,20 +42,21 @@ fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment)
/// .alignment(Alignment::Center)
/// .wrap(Wrap { trim: true });
/// ```
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Paragraph<'a> {
/// A block to wrap the widget in
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// Widget style
style: Style,
pub style: Style,
/// How to wrap the text
wrap: Option<Wrap>,
/// The text to display
pub wrap: Option<Wrap>,
/// The text to display.
/// This cannot be modified directly, only with `text()`.
text: Text<'a>,
/// Scroll
scroll: (u16, u16),
pub scroll: (u16, u16),
/// Alignment of the text
alignment: Alignment,
pub alignment: Alignment,
}
/// Describes how to wrap text across lines.
@ -92,11 +93,11 @@ pub struct Wrap {
}
impl<'a> Paragraph<'a> {
pub fn new<T>(text: T) -> Paragraph<'a>
pub fn new<T>(text: T) -> Self
where
T: Into<Text<'a>>,
{
Paragraph {
Self {
block: None,
style: Default::default(),
wrap: None,
@ -106,37 +107,44 @@ impl<'a> Paragraph<'a> {
}
}
pub fn block(mut self, block: Block<'a>) -> Paragraph<'a> {
pub fn text<T>(&mut self, text: T)
where
T: Into<Text<'a>>,
{
self.text = text.into();
}
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn style(mut self, style: Style) -> Paragraph<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn wrap(mut self, wrap: Wrap) -> Paragraph<'a> {
pub fn wrap(mut self, wrap: Wrap) -> Self {
self.wrap = Some(wrap);
self
}
pub fn scroll(mut self, offset: (u16, u16)) -> Paragraph<'a> {
pub fn scroll(mut self, offset: (u16, u16)) -> Self {
self.scroll = offset;
self
}
pub fn alignment(mut self, alignment: Alignment) -> Paragraph<'a> {
pub fn alignment(mut self, alignment: Alignment) -> Self {
self.alignment = alignment;
self
}
}
impl<'a> Widget for Paragraph<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
let text_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -162,7 +170,7 @@ impl<'a> Widget for Paragraph<'a> {
}))
});
let mut line_composer: Box<dyn LineComposer> = if let Some(Wrap { trim }) = self.wrap {
let mut line_composer: Box<dyn LineComposer<'_>> = if let Some(Wrap { trim }) = self.wrap {
Box::new(WordWrapper::new(&mut styled, text_area.width, trim))
} else {
let mut line_composer = Box::new(LineTruncator::new(&mut styled, text_area.width));

@ -13,12 +13,12 @@ pub trait LineComposer<'a> {
/// A state machine that wraps lines on word boundaries.
pub struct WordWrapper<'a, 'b> {
symbols: &'b mut dyn Iterator<Item = StyledGrapheme<'a>>,
max_line_width: u16,
current_line: Vec<StyledGrapheme<'a>>,
next_line: Vec<StyledGrapheme<'a>>,
pub symbols: &'b mut dyn Iterator<Item = StyledGrapheme<'a>>,
pub max_line_width: u16,
pub current_line: Vec<StyledGrapheme<'a>>,
pub next_line: Vec<StyledGrapheme<'a>>,
/// Removes the leading whitespace from lines
trim: bool,
pub trim: bool,
}
impl<'a, 'b> WordWrapper<'a, 'b> {
@ -26,12 +26,12 @@ impl<'a, 'b> WordWrapper<'a, 'b> {
symbols: &'b mut dyn Iterator<Item = StyledGrapheme<'a>>,
max_line_width: u16,
trim: bool,
) -> WordWrapper<'a, 'b> {
WordWrapper {
) -> Self {
Self {
symbols,
max_line_width,
current_line: vec![],
next_line: vec![],
current_line: Vec::new(),
next_line: Vec::new(),
trim,
}
}
@ -124,23 +124,23 @@ impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> {
/// A state machine that truncates overhanging lines.
pub struct LineTruncator<'a, 'b> {
symbols: &'b mut dyn Iterator<Item = StyledGrapheme<'a>>,
max_line_width: u16,
current_line: Vec<StyledGrapheme<'a>>,
pub symbols: &'b mut dyn Iterator<Item = StyledGrapheme<'a>>,
pub max_line_width: u16,
pub current_line: Vec<StyledGrapheme<'a>>,
/// Record the offet to skip render
horizontal_offset: u16,
pub horizontal_offset: u16,
}
impl<'a, 'b> LineTruncator<'a, 'b> {
pub fn new(
symbols: &'b mut dyn Iterator<Item = StyledGrapheme<'a>>,
max_line_width: u16,
) -> LineTruncator<'a, 'b> {
LineTruncator {
) -> Self {
Self {
symbols,
max_line_width,
horizontal_offset: 0,
current_line: vec![],
current_line: Vec::new(),
}
}
@ -243,7 +243,7 @@ mod test {
let style = Default::default();
let mut styled =
UnicodeSegmentation::graphemes(text, true).map(|g| StyledGrapheme { symbol: g, style });
let mut composer: Box<dyn LineComposer> = match which {
let mut composer: Box<dyn LineComposer<'_>> = match which {
Composer::WordWrapper { trim } => {
Box::new(WordWrapper::new(&mut styled, text_area_width, trim))
}

@ -0,0 +1,197 @@
use crate::{
buffer::{Buffer, Cell},
layout::Rect,
style::Style,
widgets::Widget,
};
const LOOKUP_DIGITS: [([char; 4], [char; 4], [char; 4]); 10] = [
(
['┌', '─', '─', '┐'],
['│', ' ', ' ', '│'],
['└', '─', '─', '┘'],
),
(
[' ', ' ', ' ', '╷'],
[' ', ' ', ' ', '│'],
[' ', ' ', ' ', '╵'],
),
(
['╶', '─', '─', '┐'],
['┌', '─', '─', '┘'],
['└', '─', '─', '╴'],
),
(
['╶', '─', '─', '┐'],
[' ', '─', '─', '┤'],
['╶', '─', '─', '┘'],
),
(
['╷', ' ', ' ', '╷'],
['└', '─', '─', '┤'],
[' ', ' ', ' ', '╵'],
),
(
['┌', '─', '─', '╴'],
['└', '─', '─', '┐'],
['╶', '─', '─', '┘'],
),
(
['┌', '─', '─', '╴'],
['├', '─', '─', '┐'],
['└', '─', '─', '┘'],
),
(
['╶', '─', '─', '┐'],
[' ', ' ', ' ', '│'],
[' ', ' ', ' ', '╵'],
),
(
['┌', '─', '─', '┐'],
['├', '─', '─', '┤'],
['└', '─', '─', '┘'],
),
(
['┌', '─', '─', '┐'],
['└', '─', '─', '┤'],
['╶', '─', '─', '┘'],
),
];
#[derive(Clone, Debug, Default)]
pub struct SevenSegment {
top: Vec<Cell>,
centre: Vec<Cell>,
bottom: Vec<Cell>,
}
impl SevenSegment {
/// Try to construct a seven-segment-display.
/// Such segments can only
pub fn new(text: &str) -> Result<Self, char> {
let (length, _) = text.chars().try_fold(
(0, false),
|(length, need_space), character| match character {
'0'..='9' | ' ' => Ok(if need_space {
(length + 5, true)
} else {
(length + 4, true)
}),
':' | ',' | '.' => Ok((length + 1, false)),
other => Err(other),
},
)?;
println!("Need: {}", length);
Ok(text
.chars()
.try_fold(
(
SevenSegment {
top: Vec::with_capacity(length),
centre: Vec::with_capacity(length),
bottom: Vec::with_capacity(length),
},
false,
),
|(mut display, need_space), character| match character {
'0'..='9' => {
if need_space {
display.top.push(Cell::default());
display.centre.push(Cell::default());
display.bottom.push(Cell::default());
}
let number = LOOKUP_DIGITS[character as usize - b'0' as usize];
display.top.push(number.0[0].into());
display.top.push(number.0[1].into());
display.top.push(number.0[2].into());
display.top.push(number.0[3].into());
display.centre.push(number.1[0].into());
display.centre.push(number.1[1].into());
display.centre.push(number.1[2].into());
display.centre.push(number.1[3].into());
display.bottom.push(number.2[0].into());
display.bottom.push(number.2[1].into());
display.bottom.push(number.2[2].into());
display.bottom.push(number.2[3].into());
Ok((display, true))
}
' ' => {
if need_space {
display.top.push(Cell::default());
display.centre.push(Cell::default());
display.bottom.push(Cell::default());
}
display.top.push(Cell::default());
display.top.push(Cell::default());
display.top.push(Cell::default());
display.top.push(Cell::default());
display.centre.push(Cell::default());
display.centre.push(Cell::default());
display.centre.push(Cell::default());
display.centre.push(Cell::default());
display.bottom.push(Cell::default());
display.bottom.push(Cell::default());
display.bottom.push(Cell::default());
display.bottom.push(Cell::default());
Ok((display, true))
}
':' => {
display.top.push(Cell::default());
display.centre.push(Cell {
symbol: ":".to_owned(),
..Default::default()
});
display.bottom.push(Cell::default());
Ok((display, false))
}
'.' | ',' => {
display.top.push(Cell::default());
display.centre.push(Cell::default());
display.bottom.push(Cell {
symbol: character.into(),
..Default::default()
});
Ok((display, false))
}
error => Err(error),
},
)?
.0)
}
}
impl<'a> Widget for SevenSegment {
fn render(&mut self, area: Rect, buffer: &mut Buffer) {
if area.area() > 0 {
let mut width = self.top.len();
let other = area.width as usize;
println!("{} vs. {}", width, other);
if other < width {
width = other;
}
let top = area.top();
let left = area.left();
if area.height >= 1 {
let mut index = buffer.index_of(left, top);
for cell in self.top.iter().take(width) {
buffer.content[index] = cell.to_owned();
index += 1;
}
}
if area.height >= 2 {
let mut index = buffer.index_of(left, top + 1);
for cell in self.centre.iter().take(width) {
buffer.content[index] = cell.to_owned();
index += 1;
}
}
if area.height >= 3 {
let mut index = buffer.index_of(left, top + 2);
for cell in self.bottom.iter().take(width) {
buffer.content[index] = cell.to_owned();
index += 1;
}
}
}
}
}

@ -20,64 +20,52 @@ use std::cmp::min;
/// .max(5)
/// .style(Style::default().fg(Color::Red).bg(Color::White));
/// ```
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Sparkline<'a> {
/// A block to wrap the widget in
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// Widget style
style: Style,
pub style: Style,
/// A slice of the data to display
data: &'a [u64],
pub data: &'a [u64],
/// The maximum value to take to compute the maximum bar height (if nothing is specified, the
/// widget uses the max of the dataset)
max: Option<u64>,
pub max: Option<u64>,
/// A set of bar symbols used to represent the give data
bar_set: symbols::bar::Set,
}
impl<'a> Default for Sparkline<'a> {
fn default() -> Sparkline<'a> {
Sparkline {
block: None,
style: Default::default(),
data: &[],
max: None,
bar_set: symbols::bar::NINE_LEVELS,
}
}
pub bar_set: symbols::bar::Set,
}
impl<'a> Sparkline<'a> {
pub fn block(mut self, block: Block<'a>) -> Sparkline<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn style(mut self, style: Style) -> Sparkline<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn data(mut self, data: &'a [u64]) -> Sparkline<'a> {
pub fn data(mut self, data: &'a [u64]) -> Self {
self.data = data;
self
}
pub fn max(mut self, max: u64) -> Sparkline<'a> {
pub fn max(mut self, max: u64) -> Self {
self.max = Some(max);
self
}
pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> Sparkline<'a> {
pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> Self {
self.bar_set = bar_set;
self
}
}
impl<'a> Widget for Sparkline<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
let spark_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -108,17 +96,7 @@ impl<'a> Widget for Sparkline<'a> {
.collect::<Vec<u64>>();
for j in (0..spark_area.height).rev() {
for (i, d) in data.iter_mut().enumerate() {
let symbol = match *d {
0 => self.bar_set.empty,
1 => self.bar_set.one_eighth,
2 => self.bar_set.one_quarter,
3 => self.bar_set.three_eighths,
4 => self.bar_set.half,
5 => self.bar_set.five_eighths,
6 => self.bar_set.three_quarters,
7 => self.bar_set.seven_eighths,
_ => self.bar_set.full,
};
let symbol = self.bar_set.symbol(*d as usize);
buf.get_mut(spark_area.left() + i as u16, spark_area.top() + j)
.set_symbol(symbol)
.set_style(self.style);
@ -139,7 +117,7 @@ mod tests {
#[test]
fn it_does_not_panic_if_max_is_zero() {
let widget = Sparkline::default().data(&[0, 0, 0]);
let mut widget = Sparkline::default().data(&[0, 0, 0]);
let area = Rect::new(0, 0, 3, 1);
let mut buffer = Buffer::empty(area);
widget.render(area, &mut buffer);
@ -147,7 +125,7 @@ mod tests {
#[test]
fn it_does_not_panic_if_max_is_set_to_zero() {
let widget = Sparkline::default().data(&[0, 1, 2]).max(0);
let mut widget = Sparkline::default().data(&[0, 1, 2]).max(0);
let area = Rect::new(0, 0, 3, 1);
let mut buffer = Buffer::empty(area);
widget.render(area, &mut buffer);

@ -40,7 +40,7 @@ use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Cell<'a> {
content: Text<'a>,
style: Style,
pub style: Style,
}
impl<'a> Cell<'a> {
@ -49,14 +49,21 @@ impl<'a> Cell<'a> {
self.style = style;
self
}
pub fn content<T>(&mut self, content: T)
where
T: Into<Text<'a>>,
{
self.content = content.into();
}
}
impl<'a, T> From<T> for Cell<'a>
where
T: Into<Text<'a>>,
{
fn from(content: T) -> Cell<'a> {
Cell {
fn from(content: T) -> Self {
Self {
content: content.into(),
style: Style::default(),
}
@ -84,10 +91,10 @@ where
/// By default, a row has a height of 1 but you can change this using [`Row::height`].
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Row<'a> {
cells: Vec<Cell<'a>>,
height: u16,
style: Style,
bottom_margin: u16,
pub cells: Vec<Cell<'a>>,
pub height: u16,
pub style: Style,
pub bottom_margin: u16,
}
impl<'a> Row<'a> {
@ -98,8 +105,8 @@ impl<'a> Row<'a> {
T::Item: Into<Cell<'a>>,
{
Self {
height: 1,
cells: cells.into_iter().map(|c| c.into()).collect(),
height: 1,
style: Style::default(),
bottom_margin: 0,
}
@ -185,21 +192,21 @@ impl<'a> Row<'a> {
#[derive(Debug, Clone, PartialEq)]
pub struct Table<'a> {
/// A block to wrap the widget in
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// Base style for the widget
style: Style,
pub style: Style,
/// Width constraints for each column
widths: &'a [Constraint],
/// Space between each column
column_spacing: u16,
pub column_spacing: u16,
/// Style used to render the selected row
highlight_style: Style,
pub highlight_style: Style,
/// Symbol in front of the selected rom
highlight_symbol: Option<&'a str>,
pub highlight_symbol: Option<&'a str>,
/// Optional header
header: Option<Row<'a>>,
pub header: Option<Row<'a>>,
/// Data to display in each row
rows: Vec<Row<'a>>,
pub rows: Vec<Row<'a>>,
}
impl<'a> Table<'a> {
@ -366,21 +373,12 @@ impl<'a> Table<'a> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct TableState {
offset: usize,
selected: Option<usize>,
}
impl Default for TableState {
fn default() -> TableState {
TableState {
offset: 0,
selected: None,
}
}
}
impl TableState {
pub fn selected(&self) -> Option<usize> {
self.selected
@ -397,13 +395,13 @@ impl TableState {
impl<'a> StatefulWidget for Table<'a> {
type State = TableState;
fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
fn render(&mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
if area.area() == 0 {
return;
}
buf.set_style(area, self.style);
let table_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -509,7 +507,7 @@ impl<'a> StatefulWidget for Table<'a> {
}
}
fn render_cell(buf: &mut Buffer, cell: &Cell, area: Rect) {
fn render_cell(buf: &mut Buffer, cell: &Cell<'_>, area: Rect) {
buf.set_style(area, cell.style);
for (i, spans) in cell.content.lines.iter().enumerate() {
if i as u16 >= area.height {
@ -520,7 +518,7 @@ fn render_cell(buf: &mut Buffer, cell: &Cell, area: Rect) {
}
impl<'a> Widget for Table<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default();
StatefulWidget::render(self, area, buf, &mut state);
}

@ -26,21 +26,21 @@ use crate::{
#[derive(Debug, Clone)]
pub struct Tabs<'a> {
/// A block to wrap this widget in if necessary
block: Option<Block<'a>>,
pub block: Option<Block<'a>>,
/// One title for each tab
titles: Vec<Spans<'a>>,
pub titles: Vec<Spans<'a>>,
/// The index of the selected tabs
selected: usize,
pub selected: usize,
/// The style used to draw the text
style: Style,
pub style: Style,
/// Style to apply to the selected item
highlight_style: Style,
pub highlight_style: Style,
/// Tab divider
divider: Span<'a>,
}
impl<'a> Tabs<'a> {
pub fn new(titles: Vec<Spans<'a>>) -> Tabs<'a> {
pub fn new(titles: Vec<Spans<'a>>) -> Self {
Tabs {
block: None,
titles,
@ -51,40 +51,47 @@ impl<'a> Tabs<'a> {
}
}
pub fn block(mut self, block: Block<'a>) -> Tabs<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn select(mut self, selected: usize) -> Tabs<'a> {
pub fn select(mut self, selected: usize) -> Self {
self.selected = selected;
self
}
pub fn style(mut self, style: Style) -> Tabs<'a> {
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn highlight_style(mut self, style: Style) -> Tabs<'a> {
pub fn highlight_style(mut self, style: Style) -> Self {
self.highlight_style = style;
self
}
pub fn divider<T>(mut self, divider: T) -> Tabs<'a>
pub fn divider<T>(mut self, divider: T) -> Self
where
T: Into<Span<'a>>,
{
self.divider = divider.into();
self
}
pub fn divide<T>(&mut self, divider: T)
where
T: Into<Span<'a>>,
{
self.divider = divider.into();
}
}
impl<'a> Widget for Tabs<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
fn render(&mut self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
let tabs_area = match self.block.take() {
Some(b) => {
Some(mut b) => {
let inner_area = b.inner(area);
b.render(area, buf);
inner_area
@ -98,7 +105,7 @@ impl<'a> Widget for Tabs<'a> {
let mut x = tabs_area.left();
let titles_length = self.titles.len();
for (i, title) in self.titles.into_iter().enumerate() {
for (i, title) in self.titles.iter().enumerate() {
let last_title = titles_length - 1 == i;
x = x.saturating_add(1);
let remaining_width = tabs_area.right().saturating_sub(x);

@ -0,0 +1,79 @@
use tui::{
backend::TestBackend,
buffer::Buffer,
layout::Rect,
widgets::{Block, Borders},
Terminal,
};
struct Interface {
left: Block<'static>,
right: Block<'static>,
up: Block<'static>,
down: Block<'static>,
}
#[test]
fn interface() {
let mut interface = Interface {
left: Block::default().title("_Left").borders(Borders::ALL),
right: Block::default().title("Right").borders(Borders::ALL),
up: Block::default().title("Up").borders(Borders::ALL),
down: Block::default().title("Down").borders(Borders::ALL),
};
for index in 0..5 {
interface.left.retitle(format!("{}Left", index));
let backend = TestBackend::new(24, 6);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
f.render(
&mut interface.left,
Rect {
x: 0,
y: 0,
width: 8,
height: 6,
},
);
f.render(
&mut interface.up,
Rect {
x: 8,
y: 0,
width: 8,
height: 3,
},
);
f.render(
&mut interface.down,
Rect {
x: 8,
y: 3,
width: 8,
height: 3,
},
);
f.render(
&mut interface.right,
Rect {
x: 16,
y: 0,
width: 8,
height: 6,
},
);
})
.unwrap();
let mut expected = Buffer::with_lines(vec![
"┌_Left─┐┌Up────┐┌Right─┐",
"│ ││ ││ │",
"│ │└──────┘│ │",
"│ │┌Down──┐│ │",
"│ ││ ││ │",
"└──────┘└──────┘└──────┘",
]);
expected.get_mut(1, 0).symbol = format!("{}", index);
terminal.backend().assert_buffer(&expected);
}
}

@ -0,0 +1,83 @@
use tui::{backend::TestBackend, buffer::Buffer, layout::Rect, widgets::SevenSegment, Terminal};
#[test]
fn widgets_seven_segment_full() {
let backend = TestBackend::new(60, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let display = SevenSegment::new("12:34:56.78, 90").unwrap();
f.render_widget(
display,
Rect {
x: 1,
y: 1,
width: 58,
height: 3,
},
);
})
.unwrap();
let expected = Buffer::with_lines(vec![
" ",
" ╷ ╶──┐ ╶──┐ ╷ ╷ ┌──╴ ┌──╴ ╶──┐ ┌──┐ ┌──┐ ┌──┐ ",
" │ ┌──┘: ──┤ └──┤:└──┐ ├──┐ │ ├──┤ └──┤ │ │ ",
" ╵ └──╴ ╶──┘ ╵ ╶──┘ └──┘. ╵ └──┘, ╶──┘ └──┘ ",
" ",
]);
terminal.backend().assert_buffer(&expected);
}
#[test]
fn widgets_seven_segment_half() {
let backend = TestBackend::new(28, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let display = SevenSegment::new("12:34:56.78, 90").unwrap();
f.render_widget(
display,
Rect {
x: 1,
y: 1,
width: 27,
height: 3,
},
);
})
.unwrap();
let expected = Buffer::with_lines(vec![
" ",
" ╷ ╶──┐ ╶──┐ ╷ ╷ ┌──╴ ┌─",
" │ ┌──┘: ──┤ └──┤:└──┐ ├─",
" ╵ └──╴ ╶──┘ ╵ ╶──┘ └─",
" ",
]);
terminal.backend().assert_buffer(&expected);
}
#[test]
fn widgets_seven_segment_trim() {
let backend = TestBackend::new(54, 3);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let display = SevenSegment::new("12:34:56.78, 90").unwrap();
f.render_widget(
display,
Rect {
x: 0,
y: 0,
width: 54,
height: 3,
},
);
})
.unwrap();
let expected = Buffer::with_lines(vec![
" ╷ ╶──┐ ╶──┐ ╷ ╷ ┌──╴ ┌──╴ ╶──┐ ┌──┐ ┌──┐ ┌──┐",
" │ ┌──┘: ──┤ └──┤:└──┐ ├──┐ │ ├──┤ └──┤ │ │",
" ╵ └──╴ ╶──┘ ╵ ╶──┘ └──┘. ╵ └──┘, ╶──┘ └──┘",
]);
terminal.backend().assert_buffer(&expected);
}
Loading…
Cancel
Save