Compare commits

..

17 Commits

Author SHA1 Message Date
Florian Dehau
fe17165c39 Release v0.6.0 2019-05-18 18:52:58 +02:00
svartalf
e18671c1e4 Relaxing crossterm dependency version 2019-05-18 18:49:12 +02:00
Timon_Post
b5f6219d39 updated to 0.9.4 2019-05-17 14:25:55 +02:00
Timon_Post
5ed82aac5f removed project files 2019-05-17 14:25:55 +02:00
Timon_Post
f6a0a91a23 fmt 2019-05-17 14:25:55 +02:00
timonpost@hotmail.nl
5645d0de03 gitignore 2019-05-17 14:25:55 +02:00
timonpost@hotmail.nl
ffaaf5e39c review update 2019-05-17 14:25:55 +02:00
timonpost@hotmail.nl
567cf7b8e5 update 0.9.2 2019-05-17 14:25:55 +02:00
Florian Dehau
5f8dd38135 Release v0.5.1 2019-04-14 12:18:45 +02:00
Florian Dehau
a74d335cb4 Fix clippy warnings 2019-04-14 11:48:35 +02:00
Florian Dehau
6d594143ed Format 2019-04-14 11:43:12 +02:00
Florian Dehau
7a5ad3fbdb Fix sparkline panic when max is zero 2019-04-14 11:35:41 +02:00
lcolaholicl
584f7688f4 Fix a wrongly linked link 2019-04-02 12:35:37 +02:00
Florian Dehau
4436110c44 Improve onboarding in documentation 2019-03-24 21:37:55 +01:00
lws
8a7c9d49b2 fix typo of CHANGELOG 2019-03-17 17:43:24 +01:00
lws
b5d41caace fix typo of CHANFELOG 2019-03-17 17:43:24 +01:00
Curtis Malainey
206813d560 fix typo 2019-03-11 11:58:30 +01:00
13 changed files with 116 additions and 87 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ Cargo.lock
*.log
*.rs.rustfmt
.gdb_history
.idea/

View File

@@ -2,9 +2,21 @@
## To be released
## v0.5.0 - 2019-03-10
## v0.6.0 - 2019-05-18
### Added
### Changed
* Update crossterm backend
## v0.5.1 - 2019-04-14
### Fixed
* Fix a panic in the Sparkline widget
## v0.5.0 - 2019-03-10
### Added
* Add a new curses backend (with Windows support thanks to `pancurses`).
* Add `Backend::get_cursor` and `Backend::set_cursor` methods to query and
@@ -14,7 +26,7 @@ set the position of the cursor.
* Add `Ratio` as a new variant of layout `Constraint`. It can be used to define
exact ratios constraints.
### Changed
### Changed
* Add support for multiple modifiers on the same `Style` by changing `Modifier`
from an enum to a bitflags struct.
@@ -33,14 +45,14 @@ let style = Style::default().modifier(Modifier::ITALIC);
let style = Style::default().modifier(Modifier::ITALIC | Modifier::BOLD);
```
### Fixed
### Fixed
* Ensure correct behavoir of the alternate screens with the `Crossterm` backend.
* Fix out of bounds panic when two `Buffer` are merged.
## v0.4.0 - 2019-02-03
### Added
### Added
* Add a new canvas shape: `Rectangle`.
* Official support of `Crossterm` backend.
@@ -49,11 +61,11 @@ let style = Style::default().modifier(Modifier::ITALIC | Modifier::BOLD);
* The gauge widget accepts a ratio (f64 between 0 and 1) in addition of a
percentage.
### Changed
### Changed
* Upgrade to Rust 2018 edition.
### Fixed
### Fixed
* Fix rendering of double-width characters.
* Fix race condition on the size of the terminal and expose a size that is

View File

@@ -1,6 +1,6 @@
[package]
name = "tui"
version = "0.5.0"
version = "0.6.0"
authors = ["Florian Dehau <work@fdehau.com>"]
description = """
A library to build rich terminal user interfaces or dashboards
@@ -30,7 +30,7 @@ unicode-segmentation = "1.2"
unicode-width = "0.1"
termion = { version = "1.5", optional = true }
rustbox = { version = "0.11", optional = true }
crossterm = { version = "0.6", optional = true }
crossterm = { version = "^0.9", optional = true }
easycurses = { version = "0.12.2", optional = true }
pancurses = { version = "0.16.1", optional = true, features = ["win32a"] }

View File

@@ -20,7 +20,7 @@ can either choose from:
- [crossterm](https://github.com/TimonPost/crossterm)
- [pancurses](https://github.com/ihalila/pancurses)
However, some features may only be available in one of the three.
However, some features may only be available in one of the four.
The library is based on the principle of immediate rendering with intermediate
buffers. This means that at each new frame you should build all widgets that are
@@ -56,7 +56,7 @@ The library comes with the following list of widgets:
* [Gauge](examples/gauge.rs)
* [Sparkline](examples/sparkline.rs)
* [Chart](examples/chart.rs)
* [BarChart](examples/bar_chart.rs)
* [BarChart](examples/barchart.rs)
* [List](examples/list.rs)
* [Table](examples/table.rs)
* [Paragraph](examples/paragraph.rs)

View File

@@ -31,9 +31,7 @@ fn main() -> Result<(), failure::Error> {
let cli = Cli::from_args();
stderrlog::new().quiet(!cli.log).verbosity(4).init()?;
let screen = crossterm::Screen::default();
let alternate_screen = screen.enable_alternate_modes(true)?;
let backend = CrosstermBackend::with_alternate_screen(alternate_screen)?;
let backend = CrosstermBackend::new();
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;

View File

@@ -1,26 +1,20 @@
use std::io;
use crate::backend::Backend;
use crate::buffer::Cell;
use crate::layout::Rect;
use crate::style::{Color, Modifier};
use crossterm::error::ErrorKind;
use crate::{buffer::Cell, layout::Rect};
use crossterm::{Crossterm, ErrorKind};
use std::io::{stdout, Write};
pub struct CrosstermBackend {
screen: Option<crossterm::Screen>,
crossterm: crossterm::Crossterm,
// Need to keep the AlternateScreen around even when not using it directly,
// see https://github.com/TimonPost/crossterm/issues/88
alternate_screen: Option<crossterm::AlternateScreen>,
crossterm: Crossterm,
}
impl Default for CrosstermBackend {
fn default() -> CrosstermBackend {
let screen = crossterm::Screen::default();
let crossterm = crossterm::Crossterm::from_screen(&screen);
CrosstermBackend {
screen: Some(screen),
crossterm,
crossterm: Crossterm::new(),
alternate_screen: None,
}
}
@@ -31,33 +25,15 @@ impl CrosstermBackend {
CrosstermBackend::default()
}
pub fn with_screen(screen: crossterm::Screen) -> CrosstermBackend {
let crossterm = crossterm::Crossterm::from_screen(&screen);
CrosstermBackend {
screen: Some(screen),
crossterm,
alternate_screen: None,
}
}
pub fn with_alternate_screen(
alternate_screen: crossterm::AlternateScreen,
) -> Result<CrosstermBackend, io::Error> {
let crossterm = crossterm::Crossterm::from_screen(&alternate_screen.screen);
Ok(CrosstermBackend {
screen: None,
crossterm,
crossterm: Crossterm::new(),
alternate_screen: Some(alternate_screen),
})
}
pub fn screen(&self) -> Option<&crossterm::Screen> {
match &self.screen {
Some(screen) => Some(&screen),
None => None,
}
}
pub fn alternate_screen(&self) -> Option<&crossterm::AlternateScreen> {
match &self.alternate_screen {
Some(alt_screen) => Some(&alt_screen),
@@ -89,9 +65,7 @@ fn convert_error(error: ErrorKind) -> io::Error {
impl Backend for CrosstermBackend {
fn clear(&mut self) -> io::Result<()> {
let terminal = self.crossterm.terminal();
terminal
.clear(crossterm::ClearType::All)
.map_err(convert_error)?;
terminal.clear(crossterm::ClearType::All)?;
Ok(())
}
@@ -135,6 +109,10 @@ impl Backend for CrosstermBackend {
let mut last_y = 0;
let mut last_x = 0;
let mut first = true;
let stdout = stdout();
let mut handle = stdout.lock();
for (x, y, cell) in content {
if y != last_y || x != last_x + 1 || first {
cursor.goto(x, y).map_err(convert_error)?;
@@ -151,7 +129,7 @@ impl Backend for CrosstermBackend {
}
s.object_style.attrs = cell.style.modifier.into();
self.crossterm.paint(s).map_err(convert_error)?;
write!(handle, "{}", s)?;
}
Ok(())
}

View File

@@ -54,7 +54,7 @@ impl Backend for CursesBackend {
for (col, row, cell) in content {
// eprintln!("{:?}", cell);
if row != last_row || col != last_col + 1 {
self.curses.move_rc(row as i32, col as i32);
self.curses.move_rc(i32::from(row), i32::from(col));
}
last_col = col;
last_row = row;
@@ -111,7 +111,7 @@ impl Backend for CursesBackend {
Ok((x as u16, y as u16))
}
fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
self.curses.move_rc(x as i32, y as i32);
self.curses.move_rc(i32::from(x), i32::from(y));
Ok(())
}
fn clear(&mut self) -> io::Result<()> {

View File

@@ -103,7 +103,7 @@ impl Into<rustbox::Color> for Color {
Color::Cyan | Color::LightCyan => rustbox::Color::Cyan,
Color::White => rustbox::Color::White,
Color::Blue | Color::LightBlue => rustbox::Color::Blue,
Color::Indexed(i) => rustbox::Color::Byte(i as u16),
Color::Indexed(i) => rustbox::Color::Byte(u16::from(i)),
Color::Rgb(r, g, b) => rustbox::Color::Byte(rgb_to_byte(r, g, b)),
}
}

View File

@@ -5,12 +5,34 @@
//!
//! # Get started
//!
//! ## Adding `tui` as a dependency
//!
//! ```toml
//! [dependencies]
//! tui = "0.5"
//! termion = "1.5"
//! ```
//!
//! The crate is using the `termion` backend by default but if for some reason you might want to use
//! the `rustbox` backend instead, you need the to replace your dependency specification by:
//!
//! ```toml
//! [dependencies]
//! rustbox = "0.11"
//!
//! [dependencies.tui]
//! version = "0.5"
//! default-features = false
//! features = ['rustbox']
//! ```
//!
//! The same logic applies for all other available backends.
//!
//! ## Creating a `Terminal`
//!
//! Every application using `tui` should start by instantiating a `Terminal`. It is
//! a light abstraction over available backends that provides basic functionalities
//! such as clearing the screen, hiding the cursor, etc. By default only the `termion`
//! backend is available.
//! Every application using `tui` should start by instantiating a `Terminal`. It is a light
//! abstraction over available backends that provides basic functionalities such as clearing the
//! screen, hiding the cursor, etc.
//!
//! ```rust,no_run
//! use std::io;
@@ -26,20 +48,10 @@
//! }
//! ```
//!
//! If for some reason, you might want to use the `rustbox` backend instead, you
//! need the to replace your `tui` dependency specification by:
//!
//! ```toml
//! [dependencies.tui]
//! version = "0.3.0"
//! default-features = false
//! features = ['rustbox']
//! ```
//!
//! and then create the terminal in a similar way:
//! If you had previously chosen `rustbox` as a backend, the terminal can be created in a similar
//! way:
//!
//! ```rust,ignore
//!
//! use tui::Terminal;
//! use tui::backend::RustboxBackend;
//!
@@ -50,21 +62,22 @@
//! }
//! ```
//!
//! You may also refer to the examples to find out how to create a `Terminal` for each available
//! backend.
//!
//! ## Building a User Interface (UI)
//!
//! Every component of your interface will be implementing the `Widget` trait.
//! The library comes with a predefined set of widgets that should met most of
//! your use cases. You are also free to implement your owns.
//! Every component of your interface will be implementing the `Widget` trait. The library comes
//! with a predefined set of widgets that should met most of your use cases. You are also free to
//! implement your owns.
//!
//! Each widget follows a builder pattern API providing a default configuration
//! along with methods to customize them. The widget is then registered using
//! its `render` method that take a `Frame` instance and an area to draw
//! to.
//! Each widget follows a builder pattern API providing a default configuration along with methods
//! to customize them. The widget is then registered using its `render` method that take a `Frame`
//! instance and an area to draw to.
//!
//! The following example renders a block of the size of the terminal:
//!
//! ```rust,no_run
//!
//! use std::io;
//! use termion::raw::IntoRawMode;
//! use tui::Terminal;
@@ -93,7 +106,6 @@
//! full customization. And `Layout` is no exception:
//!
//! ```rust,no_run
//!
//! use std::io;
//! use termion::raw::IntoRawMode;
//! use tui::Terminal;
@@ -129,10 +141,10 @@
//! }
//! ```
//!
//! This let you describe responsive terminal UI by nesting layouts. You should note
//! that by default the computed layout tries to fill the available space
//! completely. So if for any reason you might need a blank space somewhere, try to
//! pass an additional constraint and don't use the corresponding area.
//! This let you describe responsive terminal UI by nesting layouts. You should note that by
//! default the computed layout tries to fill the available space completely. So if for any reason
//! you might need a blank space somewhere, try to pass an additional constraint and don't use the
//! corresponding area.
pub mod backend;
pub mod buffer;

View File

@@ -26,7 +26,7 @@ pub enum Color {
impl Color {
/// Returns a short code associated with the color, used for debug purpose
/// only
pub(crate) fn code(&self) -> &str {
pub(crate) fn code(self) -> &'static str {
match self {
Color::Reset => "X",
Color::Black => "b",
@@ -68,7 +68,7 @@ bitflags! {
impl Modifier {
/// Returns a short code associated with the color, used for debug purpose
/// only
pub(crate) fn code(&self) -> String {
pub(crate) fn code(self) -> String {
use std::fmt::Write;
let mut result = String::new();

View File

@@ -1,5 +1,5 @@
use std::iter;
use std::iter::Iterator;
use std::convert::AsRef;
use std::iter::{self, Iterator};
use unicode_width::UnicodeWidthStr;
@@ -166,7 +166,7 @@ impl<'b> SelectableList<'b> {
where
I: AsRef<str> + 'b,
{
self.items = items.iter().map(|i| i.as_ref()).collect::<Vec<&str>>();
self.items = items.iter().map(AsRef::as_ref).collect::<Vec<&str>>();
self
}

View File

@@ -89,7 +89,13 @@ impl<'a> Widget for Sparkline<'a> {
.data
.iter()
.take(max_index)
.map(|e| e * u64::from(spark_area.height) * 8 / max)
.map(|e| {
if max != 0 {
e * u64::from(spark_area.height) * 8 / max
} else {
0
}
})
.collect::<Vec<u64>>();
for j in (0..spark_area.height).rev() {
for (i, d) in data.iter_mut().enumerate() {
@@ -118,3 +124,24 @@ impl<'a> Widget for Sparkline<'a> {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_does_not_panic_if_max_is_zero() {
let mut widget = Sparkline::default().data(&[0, 0, 0]);
let area = Rect::new(0, 0, 3, 1);
let mut buffer = Buffer::empty(area);
widget.draw(area, &mut buffer);
}
#[test]
fn it_does_not_panic_if_max_is_set_to_zero() {
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.draw(area, &mut buffer);
}
}

View File

@@ -82,7 +82,8 @@ fn paragraph_render_double_width() {
let backend = TestBackend::new(10, 10);
let mut terminal = Terminal::new(backend).unwrap();
let s = "コンピュータ上で文字を扱う場合、典型的には文字による通信を行う場合にその両端点では、";
let s =
"コンピュータ上で文字を扱う場合、典型的には文字による通信を行う場合にその両端点では、";
terminal
.draw(|mut f| {
let size = f.size();