Merge branch 'master' into master

pull/585/head
StratusFearMe21 2 years ago committed by GitHub
commit b06cfa1098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,7 +9,7 @@ on:
name: CI
env:
CI_CARGO_MAKE_VERSION: 0.35.8
CI_CARGO_MAKE_VERSION: 0.35.16
jobs:
linux:

@ -2,6 +2,18 @@
## To be released
## v0.19.0 - 2022-08-14
### Features
* Bump `crossterm` to `0.25`
## v0.18.0 - 2022-04-24
### Features
* Update `crossterm` to `0.23`
## v0.17.0 - 2022-01-22
### Features

@ -1,11 +1,11 @@
[package]
name = "tui"
version = "0.17.0"
version = "0.19.0"
authors = ["Florian Dehau <work@fdehau.com>"]
description = """
A library to build rich terminal user interfaces or dashboards
"""
documentation = "https://docs.rs/tui/0.17.0/tui/"
documentation = "https://docs.rs/tui/0.19.0/tui/"
keywords = ["tui", "terminal", "dashboard"]
repository = "https://github.com/fdehau/tui-rs"
readme = "README.md"
@ -20,6 +20,7 @@ exclude = [
]
autoexamples = true
edition = "2021"
rust-version = "1.56.1"
[badges]
@ -72,6 +73,10 @@ required-features = ["crossterm"]
name = "list"
required-features = ["crossterm"]
[[example]]
name = "panic"
required-features = ["crossterm"]
[[example]]
name = "paragraph"
required-features = ["crossterm"]

@ -26,6 +26,8 @@ comes from the terminal emulator than the library itself.
Moreover, the library does not provide any input handling nor any event system and
you may rely on the previously cited libraries to achieve such features.
**I'm actively looking for help maintaining this crate. See [this issue](https://github.com/fdehau/tui-rs/issues/654)**
### Rust version requirements
Since version 0.17.0, `tui` requires **rustc version 1.56.1 or greater**.
@ -45,8 +47,8 @@ cargo run --example demo --no-default-features --features=termion --release -- -
where `tick-rate` is the UI refresh rate in ms.
The UI code is in [examples/demo/ui.rs](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/demo/ui.rs) while the
application state is in [examples/demo/app.rs](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/demo/app.rs).
The UI code is in [examples/demo/ui.rs](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/demo/ui.rs) while the
application state is in [examples/demo/app.rs](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/demo/app.rs).
If the user interface contains glyphs that are not displayed correctly by your terminal, you may want to run
the demo without those symbols:
@ -59,16 +61,16 @@ cargo run --example demo --release -- --tick-rate 200 --enhanced-graphics false
The library comes with the following list of widgets:
* [Block](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/block.rs)
* [Gauge](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/gauge.rs)
* [Sparkline](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/sparkline.rs)
* [Chart](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/chart.rs)
* [BarChart](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/barchart.rs)
* [List](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/list.rs)
* [Table](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/table.rs)
* [Paragraph](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/paragraph.rs)
* [Canvas (with line, point cloud, map)](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/canvas.rs)
* [Tabs](https://github.com/fdehau/tui-rs/blob/v0.17.0/examples/tabs.rs)
* [Block](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/block.rs)
* [Gauge](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/gauge.rs)
* [Sparkline](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/sparkline.rs)
* [Chart](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/chart.rs)
* [BarChart](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/barchart.rs)
* [List](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/list.rs)
* [Table](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/table.rs)
* [Paragraph](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/paragraph.rs)
* [Canvas (with line, point cloud, map)](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/canvas.rs)
* [Tabs](https://github.com/fdehau/tui-rs/blob/v0.19.0/examples/tabs.rs)
Click on each item to see the source of the example. Run the examples with with
cargo (e.g. to run the gauge example `cargo run --example gauge`), and quit by pressing `q`.
@ -79,6 +81,8 @@ You can run all examples by running `cargo make run-examples` (require
### Third-party widgets
* [tui-logger](https://github.com/gin66/tui-logger)
* [tui-textarea](https://github.com/rhysd/tui-textarea): simple yet powerful multi-line text editor widget supporting several key shortcuts, undo/redo, text search, etc.
* [tui-rs-tree-widgets](https://github.com/EdJoPaTo/tui-rs-tree-widget): widget for tree data structures.
### Apps using tui
@ -108,6 +112,17 @@ You can run all examples by running `cargo make run-examples` (require
* [joshuto](https://github.com/kamiyaa/joshuto)
* [adsb_deku/radar](https://github.com/wcampbell0x2a/adsb_deku#radar-tui)
* [hoard](https://github.com/Hyde46/hoard)
* [tokio-console](https://github.com/tokio-rs/console): a diagnostics and debugging tool for asynchronous Rust programs.
* [hwatch](https://github.com/blacknon/hwatch): a alternative watch command that records the result of command execution and can display its history and diffs.
* [ytui-music](https://github.com/sudipghimire533/ytui-music): listen to music from youtube inside your terminal.
* [mqttui](https://github.com/EdJoPaTo/mqttui): subscribe or publish to a MQTT Topic quickly from the terminal.
* [meteo-tui](https://github.com/16arpi/meteo-tui): french weather via the command line.
* [picterm](https://github.com/ksk001100/picterm): preview images in your terminal.
* [gobang](https://github.com/TaKO8Ki/gobang): a cross-platform TUI database management tool.
* [oxker](https://github.com/mrjackwills/oxker): a simple tui to view & control docker containers.
* [trippy](https://github.com/fujiapple852/trippy): a network diagnostic tool.
* [cotp](https://github.com/replydev/cotp): a trustworthy, encrypted, command-line TOTP/HOTP authenticator app with import functionality.
* [hg-tui](https://github.com/kaixinbaba/hg-tui): view [hellogithub.com](https://hellogithub.com/) website on the terminal.
### Alternatives

@ -0,0 +1,142 @@
//! How to use a panic hook to reset the terminal before printing the panic to
//! the terminal.
//!
//! When exiting normally or when handling `Result::Err`, we can reset the
//! terminal manually at the end of `main` just before we print the error.
//!
//! Because a panic interrupts the normal control flow, manually resetting the
//! terminal at the end of `main` won't do us any good. Instead, we need to
//! make sure to set up a panic hook that first resets the terminal before
//! handling the panic. This both reuses the standard panic hook to ensure a
//! consistent panic handling UX and properly resets the terminal to not
//! distort the output.
//!
//! That's why this example is set up to show both situations, with and without
//! the chained panic hook, to see the difference.
#![deny(clippy::all)]
#![warn(clippy::pedantic, clippy::nursery)]
use std::error::Error;
use std::io;
use crossterm::event::{self, Event, KeyCode};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
use tui::backend::{Backend, CrosstermBackend};
use tui::layout::Alignment;
use tui::text::Spans;
use tui::widgets::{Block, Borders, Paragraph};
use tui::{Frame, Terminal};
type Result<T> = std::result::Result<T, Box<dyn Error>>;
#[derive(Default)]
struct App {
hook_enabled: bool,
}
impl App {
fn chain_hook(&mut self) {
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic| {
reset_terminal().unwrap();
original_hook(panic);
}));
self.hook_enabled = true;
}
}
fn main() -> Result<()> {
let mut terminal = init_terminal()?;
let mut app = App::default();
let res = run_tui(&mut terminal, &mut app);
reset_terminal()?;
if let Err(err) = res {
println!("{:?}", err);
}
Ok(())
}
/// Initializes the terminal.
fn init_terminal() -> Result<Terminal<CrosstermBackend<io::Stdout>>> {
crossterm::execute!(io::stdout(), EnterAlternateScreen)?;
enable_raw_mode()?;
let backend = CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
Ok(terminal)
}
/// Resets the terminal.
fn reset_terminal() -> Result<()> {
disable_raw_mode()?;
crossterm::execute!(io::stdout(), LeaveAlternateScreen)?;
Ok(())
}
/// Runs the TUI loop.
fn run_tui<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
loop {
terminal.draw(|f| ui(f, app))?;
if let Event::Key(key) = event::read()? {
match key.code {
KeyCode::Char('p') => {
panic!("intentional demo panic");
}
KeyCode::Char('e') => {
app.chain_hook();
}
_ => {
return Ok(());
}
}
}
}
}
/// Render the TUI.
fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
let text = vec![
if app.hook_enabled {
Spans::from("HOOK IS CURRENTLY **ENABLED**")
} else {
Spans::from("HOOK IS CURRENTLY **DISABLED**")
},
Spans::from(""),
Spans::from("press `p` to panic"),
Spans::from("press `e` to enable the terminal-resetting panic hook"),
Spans::from("press any other key to quit without panic"),
Spans::from(""),
Spans::from("when you panic without the chained hook,"),
Spans::from("you will likely have to reset your terminal afterwards"),
Spans::from("with the `reset` command"),
Spans::from(""),
Spans::from("with the chained panic hook enabled,"),
Spans::from("you should see the panic report as you would without tui"),
Spans::from(""),
Spans::from("try first without the panic handler to see the difference"),
];
let b = Block::default()
.title("Panic Handler Demo")
.borders(Borders::ALL);
let p = Paragraph::new(text).block(b).alignment(Alignment::Center);
f.render_widget(p, f.size());
}

@ -8,7 +8,7 @@ use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
/// A buffer cell
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cell {
pub symbol: String,
pub fg: Color,
@ -105,7 +105,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, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Buffer {
/// The area represented by this buffer
pub area: Rect,

@ -119,7 +119,7 @@ pub struct Margin {
pub horizontal: u16,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Alignment {
Left,
Center,
@ -494,7 +494,7 @@ impl Element {
}
}
/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// A simple rectangle used in the computation of the layout and to give widgets a hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
pub struct Rect {

@ -9,8 +9,8 @@
//!
//! ```toml
//! [dependencies]
//! tui = "0.17"
//! crossterm = "0.22"
//! tui = "0.19"
//! crossterm = "0.25"
//! ```
//!
//! The crate is using the `crossterm` backend by default that works on most platforms. But if for
@ -20,7 +20,7 @@
//! ```toml
//! [dependencies]
//! termion = "1.5"
//! tui = { version = "0.17", default-features = false, features = ['termion'] }
//! tui = { version = "0.19", default-features = false, features = ['termion'] }
//!
//! ```
//!

@ -2,7 +2,7 @@
use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Color {
Reset,
@ -115,7 +115,7 @@ bitflags! {
/// buffer.get(0, 0).style(),
/// );
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Style {
pub fg: Option<Color>,

@ -52,14 +52,14 @@ use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
/// A grapheme associated to a style.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StyledGrapheme<'a> {
pub symbol: &'a str,
pub style: Style,
}
/// A string where all graphemes have the same style.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span<'a> {
pub content: Cow<'a, str>,
pub style: Style,
@ -194,7 +194,7 @@ impl<'a> From<&'a str> for Span<'a> {
}
/// A string composed of clusters of graphemes, each with their own style.
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Default, Eq)]
pub struct Spans<'a>(pub Vec<Span<'a>>);
impl<'a> Spans<'a> {
@ -273,7 +273,7 @@ impl<'a> From<Spans<'a>> for String {
/// text.extend(Text::styled("Some more lines\nnow with more style!", style));
/// assert_eq!(6, text.height());
/// ```
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Default, Eq)]
pub struct Text<'a> {
pub lines: Vec<Spans<'a>>,
}

@ -7,7 +7,7 @@ use crate::{
widgets::{Borders, Widget},
};
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BorderType {
Plain,
Rounded,
@ -41,7 +41,7 @@ impl BorderType {
/// .border_type(BorderType::Rounded)
/// .style(Style::default().bg(Color::Black));
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Block<'a> {
/// Optional title place on the upper left of the block
title: Option<Spans<'a>>,

@ -26,7 +26,7 @@ impl ListState {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListItem<'a> {
content: Text<'a>,
style: Style,

@ -489,7 +489,7 @@ mod test {
assert_eq!(word_wrapper, vec!["AAAAAAAAAAAAAAA", "AAAA\u{00a0}AAA",]);
// Ensure that if the character was a regular space, it would be wrapped differently.
let text_space = text.replace("\u{00a0}", " ");
let text_space = text.replace('\u{00a0}', " ");
let (word_wrapper_space, _) =
run_composer(Composer::WordWrapper { trim: true }, &text_space, width);
assert_eq!(word_wrapper_space, vec!["AAAAAAAAAAAAAAA AAAA", "AAA",]);

@ -31,7 +31,7 @@ use unicode_width::UnicodeWidthStr;
///
/// You can apply a [`Style`] on the entire [`Cell`] using [`Cell::style`] or rely on the styling
/// capabilities of [`Text`].
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Cell<'a> {
content: Text<'a>,
style: Style,
@ -86,7 +86,7 @@ where
/// ```
///
/// By default, a row has a height of 1 but you can change this using [`Row::height`].
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Row<'a> {
cells: Vec<Cell<'a>>,
height: u16,
@ -186,7 +186,7 @@ impl<'a> Row<'a> {
/// // ...and potentially show a symbol in front of the selection.
/// .highlight_symbol(">>");
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Table<'a> {
/// A block to wrap the widget in
block: Option<Block<'a>>,

Loading…
Cancel
Save