Defaults and little things

pull/454/head
Sebastian Walz 3 years ago
commit 9fd9b0b3a4
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,7 +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)
* [Vector](https://vector.dev)
### Alternatives

@ -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,
{
@ -263,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,
@ -314,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.

@ -168,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(

@ -28,7 +28,7 @@ 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,

@ -90,7 +90,7 @@ impl Default for GraphType {
}
/// 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).
/// Cannot be modified directly, only with `rename()`.
@ -105,18 +105,6 @@ pub struct Dataset<'a> {
pub 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(),
}
}
}
impl<'a> Dataset<'a> {
pub fn name<S>(mut self, name: S) -> Self
where
@ -212,7 +200,7 @@ struct 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
pub block: Option<Block<'a>>,

@ -172,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.
@ -200,7 +190,7 @@ fn get_unicode_block<'a>(frac: f64) -> &'a str {
/// .line_set(symbols::line::THICK)
/// .ratio(0.4);
/// ```
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct LineGauge<'a> {
pub block: Option<Block<'a>>,
/// Cannot be modified directly, only with `set_percent()` and `set_ration()`.
@ -211,19 +201,6 @@ pub struct LineGauge<'a> {
pub 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(),
}
}
}
impl<'a> LineGauge<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);

@ -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;

@ -42,7 +42,7 @@ 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
pub block: Option<Block<'a>>,

@ -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,7 +20,7 @@ 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
pub block: Option<Block<'a>>,
@ -35,18 +35,6 @@ pub struct Sparkline<'a> {
pub 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,
}
}
}
impl<'a> Sparkline<'a> {
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
@ -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);

@ -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