# Changelog ## 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 * Add option to `widgets::List` to repeat the hightlight symbol for each line of multi-line items (#533). * Add option to control the alignment of `Axis` labels in the `Chart` widget (#568). ### Breaking changes * The minimum supported rust version is now `1.56.1`. #### New default backend and consolidated backend options (#553) * `crossterm` is now the default backend. If you are already using the `crossterm` backend, you can simplify your dependency specification in `Cargo.toml`: ```diff - tui = { version = "0.16", default-features = false, features = ["crossterm"] } + tui = "0.17" ``` If you are using the `termion` backend, your `Cargo` is now a bit more verbose: ```diff - tui = "0.16" + tui = { version = "0.17", default-features = false, features = ["termion"] } ``` `crossterm` has also been bumped to version `0.22`. Because of their apparent low usage, `curses` and `rustbox` backends have been removed. If you are using one of them, you can import their last implementation in your own project: * [curses](https://github.com/fdehau/tui-rs/blob/v0.16.0/src/backend/curses.rs) * [rustbox](https://github.com/fdehau/tui-rs/blob/v0.16.0/src/backend/rustbox.rs) #### Canvas labels (#543) * Labels of the `Canvas` widget are now `text::Spans`. The signature of `widgets::canvas::Context::print` has thus been updated: ```diff - ctx.print(x, y, "Some text", Color::Yellow); + ctx.print(x, y, Span::styled("Some text", Style::default().fg(Color::Yellow))) ``` ## v0.16.0 - 2021-08-01 ### Features * Update `crossterm` to `0.20`. * Add `From>` implementation for `text::Text` (#471). * Add option to right or center align the title of a `widgets::Block` (#462). ### Fixes * Apply label style in `widgets::Gauge` and avoid panics because of overflows with long labels (#494). * Avoid panics because of overflows with long axis labels in `widgets::Chart` (#512). * Fix computation of column widths in `widgets::Table` (#514). * Fix panics because of invalid offset when input changes between two frames in `widgets::List` and `widgets::Chart` (#516). ## v0.15.0 - 2021-05-02 ### Features * Update `crossterm` to `0.19`. * Update `rand` to `0.8`. * Add a read-only view of the terminal state after the draw call (#440). ### Fixes * Remove compile warning in `TestBackend::assert_buffer` (#466). ## v0.14.0 - 2021-01-01 ### Breaking changes #### New API for the Table widget The `Table` widget got a lot of improvements that should make it easier to work with: * It should not longer panic when rendered on small areas. * `Row`s are now a collection of `Cell`s, themselves wrapping a `Text`. This means you can style the entire `Table`, an entire `Row`, an entire `Cell` and rely on the styling capabilities of `Text` to get full control over the look of your `Table`. * `Row`s can have multiple lines. * The header is now optional and is just another `Row` always visible at the top. * `Row`s can have a bottom margin. * The header alignment is no longer off when an item is selected. Taking the example of the code in `examples/demo/ui.rs`, this is what you may have to change: ```diff let failure_style = Style::default() .fg(Color::Red) .add_modifier(Modifier::RAPID_BLINK | Modifier::CROSSED_OUT); - let header = ["Server", "Location", "Status"]; let rows = app.servers.iter().map(|s| { let style = if s.status == "Up" { up_style } else { failure_style }; - Row::StyledData(vec![s.name, s.location, s.status].into_iter(), style) + Row::new(vec![s.name, s.location, s.status]).style(style) }); - let table = Table::new(header.iter(), rows) + let table = Table::new(rows) + .header( + Row::new(vec!["Server", "Location", "Status"]) + .style(Style::default().fg(Color::Yellow)) + .bottom_margin(1), + ) .block(Block::default().title("Servers").borders(Borders::ALL)) - .header_style(Style::default().fg(Color::Yellow)) .widths(&[ Constraint::Length(15), Constraint::Length(15), ``` Here, we had to: - Change the way we construct [`Row`](https://docs.rs/tui/*/tui/widgets/struct.Row.html) which is no longer an `enum` but a `struct`. It accepts anything that can be converted to an iterator of things that can be converted to a [`Cell`](https://docs.rs/tui/*/tui/widgets/struct.Cell.html) - The header is no longer a required parameter so we use [`Table::header`](https://docs.rs/tui/*/tui/widgets/struct.Table.html#method.header) to set it. `Table::header_style` has been removed since the style can be directly set using [`Row::style`](https://docs.rs/tui/*/tui/widgets/struct.Row.html#method.style). In addition, we want to preserve the old margin between the header and the rest of the rows so we add a bottom margin to the header using [`Row::bottom_margin`](https://docs.rs/tui/*/tui/widgets/struct.Row.html#method.bottom_margin). You may want to look at the documentation of the different types to get a better understanding: - [`Table`](https://docs.rs/tui/*/tui/widgets/struct.Table.html) - [`Row`](https://docs.rs/tui/*/tui/widgets/struct.Row.html) - [`Cell`](https://docs.rs/tui/*/tui/widgets/struct.Cell.html) ### Fixes - Fix handling of Non Breaking Space (NBSP) in wrapped text in `Paragraph` widget. ### Features - Add `Style::reset` to create a `Style` resetting all styling properties when applied. - Add an option to render the `Gauge` widget with unicode blocks. - Manage common project tasks with `cargo-make` rather than `make` for easier on-boarding. ## v0.13.0 - 2020-11-14 ### Features * Add `LineGauge` widget which is a more compact variant of the existing `Gauge`. * Bump `crossterm` to 0.18 ### Fixes * Take into account the borders of the `Table` widget when the widths of columns is controlled by `Percentage` and `Ratio` constraints. ## v0.12.0 - 2020-09-27 ### Features * Make it easier to work with string with multiple lines in `Text` (#361). ### Fixes * Fix a style leak in `Graph` so components drawn on top of the plotted data (i.e legend and axis titles) are not affected by the style of the `Dataset`s (#388). * Make sure `BarChart` shows bars with the max height only when the plotted data is actually equal to the max (#383). ## v0.11.0 - 2020-09-20 ### Features * Add the dot character as a new type of canvas marker (#350). * Support more style modifiers on Windows (#368). ### Fixes * Clearing the terminal through `Terminal::clear` will cause the whole UI to be redrawn (#380). * Fix incorrect output when the first diff to draw is on the second cell of the terminal (#347). ## v0.10.0 - 2020-07-17 ### Breaking changes #### Easier cursor management A new method has been added to `Frame` called `set_cursor`. It lets you specify where the cursor should be placed after the draw call. Furthermore like any other widgets, if you do not set a cursor position during a draw call, the cursor is automatically hidden. For example: ```rust fn draw_input(f: &mut Frame, app: &App) { if app.editing { let input_width = app.input.width() as u16; // The cursor will be placed just after the last character of the input f.set_cursor((input_width + 1, 0)); } else { // We are no longer editing, the cursor does not have to be shown, set_cursor is not called and // thus automatically hidden. } } ``` In order to make this possible, the draw closure takes in input `&mut Frame` instead of `mut Frame`. #### Advanced text styling It has been reported several times that the text styling capabilities were somewhat limited in many places of the crate. To solve the issue, this release includes a new set of text primitives that are now used by a majority of widgets to provide flexible text styling. `Text` is replaced by the following types: - `Span`: a string with a unique style. - `Spans`: a string with multiple styles. - `Text`: a multi-lines string with multiple styles. However, you do not always need this complexity so the crate provides `From` implementations to let you use simple strings as a default and switch to the previous primitives when you need additional styling capabilities. For example, the title of a `Block` can be set in the following ways: ```rust // A title with no styling Block::default().title("My title"); // A yellow title Block::default().title(Span::styled("My title", Style::default().fg(Color::Yellow))); // A title where "My" is bold and "title" is a simple string Block::default().title(vec![ Span::styled("My", Style::default().add_modifier(Modifier::BOLD)), Span::from("title") ]); ``` - `Buffer::set_spans` and `Buffer::set_span` were added. - `Paragraph::new` expects an input that can be converted to a `Text`. - `Block::title_style` is deprecated. - `Block::title` expects a `Spans`. - `Tabs` expects a list of `Spans`. - `Gauge` custom label is now a `Span`. - `Axis` title and labels are `Spans` (as a consequence `Chart` no longer has generic bounds). #### Incremental styling Previously `Style` was used to represent an exhaustive set of style rules to be applied to an UI element. It implied that whenever you wanted to change even only one property you had to provide the complete style. For example, if you had a `Block` where you wanted to have a green background and a title in bold, you had to do the following: ```rust let style = Style::default().bg(Color::Green); Block::default() .style(style) .title("My title") // Here we reused the style otherwise the background color would have been reset .title_style(style.modifier(Modifier::BOLD)); ``` In this new release, you may now write this as: ```rust Block::default() .style(Style::default().bg(Color::Green)) // The style is not overidden anymore, we simply add new style rule for the title. .title(Span::styled("My title", Style::default().add_modifier(Modifier::BOLD))) ``` In addition, the crate now provides a method `patch` to combine two styles into a new set of style rules: ```rust let style = Style::default().modifer(Modifier::BOLD); let style = style.patch(Style::default().add_modifier(Modifier::ITALIC)); // style.modifer == Modifier::BOLD | Modifier::ITALIC, the modifier has been enriched not overidden ``` - `Style::modifier` has been removed in favor of `Style::add_modifier` and `Style::remove_modifier`. - `Buffer::set_style` has been added. `Buffer::set_background` is deprecated. - `BarChart::style` no longer set the style of the bars. Use `BarChart::bar_style` in replacement. - `Gauge::style` no longer set the style of the gauge. Use `Gauge::gauge_style` in replacement. #### List with item on multiple lines The `List` widget has been refactored once again to support items with variable heights and complex styling. - `List::new` expects an input that can be converted to a `Vec` where `ListItem` is a wrapper around the item content to provide additional styling capabilities. `ListItem` contains a `Text`. - `List::items` has been removed. ```rust // Before let items = vec![ "Item1", "Item2", "Item3" ]; List::default().items(items.iters()); // After let items = vec![ ListItem::new("Item1"), ListItem::new("Item2"), ListItem::new("Item3"), ]; List::new(items); ``` See the examples for more advanced usages. #### More wrapping options `Paragraph::wrap` expects `Wrap` instead of `bool` to let users decided whether they want to trim whitespaces when the text is wrapped. ```rust // before Paragraph::new(text).wrap(true) // after Paragraph::new(text).wrap(Wrap { trim: true }) // to have the same behavior Paragraph::new(text).wrap(Wrap { trim: false }) // to use the new behavior ``` #### Horizontal scrolling in paragraph You can now scroll horizontally in `Paragraph`. The argument of `Paragraph::scroll` has thus be changed from `u16` to `(u16, u16)`. ### Features #### Serialization of style You can now serialize and de-serialize `Style` using the optional `serde` feature. ## v0.9.5 - 2020-05-21 ### Bug Fixes * Fix out of bounds panic in `widgets::Tabs` when the widget is rendered on small areas. ## v0.9.4 - 2020-05-12 ### Bug Fixes * Ignore zero-width graphemes in `Buffer::set_stringn`. ## v0.9.3 - 2020-05-11 ### Bug Fixes * Fix usize overflows in `widgets::Chart` when a dataset is empty. ## v0.9.2 - 2020-05-10 ### Bug Fixes * Fix usize overflows in `widgets::canvas::Line` drawing algorithm. ## v0.9.1 - 2020-04-16 ### Bug Fixes * The `List` widget now takes into account the width of the `highlight_symbol` when calculating the total width of its items. It prevents items to overflow outside of the widget area. ## v0.9.0 - 2020-04-14 ### Features * Introduce stateful widgets, i.e widgets that can take advantage of keeping some state around between two draw calls (#210 goes a bit more into the details). * Allow a `Table` row to be selected. ```rust // State initialization let mut state = TableState::default(); // In the terminal.draw closure let header = ["Col1", "Col2", "Col"]; let rows = [ Row::Data(["Row11", "Row12", "Row13"].into_iter()) ]; let table = Table::new(header.into_iter(), rows.into_iter()); f.render_stateful_widget(table, area, &mut state); // In response to some event: state.select(Some(1)); ``` * Add a way to choose the type of border used to draw a block. You can now choose from plain, rounded, double and thick lines. * Add a `graph_type` property on the `Dataset` of a `Chart` widget. By default it will be `Scatter` where the points are drawn as is. An other option is `Line` where a line will be draw between each consecutive points of the dataset. * Style methods are now const, allowing you to initialize const `Style` objects. * Improve control over whether the legend in the `Chart` widget is shown or not. You can now set custom constraints using `Chart::hidden_legend_constraints`. * Add `Table::header_gap` to add some space between the header and the first row. * Remove `log` from the dependencies * Add a way to use a restricted set of unicode symbols in several widgets to improve portability in exchange of a degraded output. (see `BarChart::bar_set`, `Sparkline::bar_set` and `Canvas::marker`). You can check how the `--enhanced-graphics` flag is used in the demos. ### Breaking Changes * `Widget::render` has been deleted. You should now use `Frame::render_widget` to render a widget on the corresponding `Frame`. This makes the `Widget` implementation totally decoupled from the `Frame`. ```rust // Before Block::default().render(&mut f, size); // After let block = Block::default(); f.render_widget(block, size); ``` * `Widget::draw` has been renamed to `Widget::render` and the signature has been updated to reflect that widgets are consumable objects. Thus the method takes `self` instead of `&mut self`. ```rust // Before impl Widget for MyWidget { fn draw(&mut self, area: Rect, buf: &mut Buffer) { } } /// After impl Widget for MyWidget { fn render(self, arera: Rect, buf: &mut Buffer) { } } ``` * `Widget::background` has been replaced by `Buffer::set_background` ```rust // Before impl Widget for MyWidget { fn render(self, arera: Rect, buf: &mut Buffer) { self.background(area, buf, self.style.bg); } } // After impl Widget for MyWidget { fn render(self, arera: Rect, buf: &mut Buffer) { buf.set_background(area, self.style.bg); } } ``` * Update the `Shape` trait for objects that can be draw on a `Canvas` widgets. Instead of returning an iterator over its points, a `Shape` is given a `Painter` object that provides a `paint` as well as a `get_point` method. This gives the `Shape` more information about the surface it will be drawn to. In particular, this change allows the `Line` shape to use a more precise and efficient drawing algorithm (Bresenham's line algorithm). * `SelectableList` has been deleted. You can now take advantage of the associated `ListState` of the `List` widget to select an item. ```rust // Before List::new(&["Item1", "Item2", "Item3"]) .select(Some(1)) .render(&mut f, area); // After // State initialization let mut state = ListState::default(); // In the terminal.draw closure let list = List::new(&["Item1", "Item2", "Item3"]); f.render_stateful_widget(list, area, &mut state); // In response to some events state.select(Some(1)); ``` * `widgets::Marker` has been moved to `symbols::Marker` ## v0.8.0 - 2019-12-15 ### Breaking Changes * Bump crossterm to 0.14. * Add cross symbol to the symbols list. ### Bug Fixes * Use the value of `title_style` to style the title of `Axis`. ## v0.7.0 - 2019-11-29 ### Breaking Changes * Use `Constraint` instead of integers to specify the widths of the `Table` widget's columns. This will allow more responsive tables. ```rust Table::new(header, row) .widths(&[15, 15, 10]) .render(f, chunk); ``` becomes: ```rust Table::new(header, row) .widths(&[ Constraint::Length(15), Constraint::Length(15), Constraint::Length(10), ]) .render(f, chunk); ``` * Bump crossterm to 0.13. * Use Github Actions for CI (Travis and Azure Pipelines integrations have been deleted). ### Features * Add support for horizontal and vertical margins in `Layout`. ## v0.6.2 - 2019-07-16 ### Features * `Text` implements PartialEq ### Bug Fixes * Avoid overflow errors in canvas ## v0.6.1 - 2019-06-16 ### Bug Fixes * Avoid a division by zero when all values in a barchart are equal to 0. * Fix the inverted cursor position in the curses backend. * Ensure that the correct terminal size is returned when using the crossterm backend. * Avoid highlighting the separator after the selected item in the Tabs widget. ## v0.6.0 - 2019-05-18 ### Breaking Changes * Update crossterm backend ## v0.5.1 - 2019-04-14 ### Bug Fixes * Fix a panic in the Sparkline widget ## v0.5.0 - 2019-03-10 ### Features * Add a new curses backend (with Windows support thanks to `pancurses`). * Add `Backend::get_cursor` and `Backend::set_cursor` methods to query and set the position of the cursor. * Add more constructors to the `Crossterm` backend. * Add a demo for all backends using a shared UI and application state. * Add `Ratio` as a new variant of layout `Constraint`. It can be used to define exact ratios constraints. ### Breaking Changes * Add support for multiple modifiers on the same `Style` by changing `Modifier` from an enum to a bitflags struct. So instead of writing: ```rust let style = Style::default().add_modifier(Modifier::Italic); ``` one should use: ```rust let style = Style::default().add_modifier(Modifier::ITALIC); // or let style = Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD); ``` ### Bug Fixes * 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 ### Features * Add a new canvas shape: `Rectangle`. * Official support of `Crossterm` backend. * Make it possible to choose the divider between `Tabs`. * Add word wrapping on Paragraph. * The gauge widget accepts a ratio (f64 between 0 and 1) in addition of a percentage. ### Breaking Changes * Upgrade to Rust 2018 edition. ### Bug Fixes * Fix rendering of double-width characters. * Fix race condition on the size of the terminal and expose a size that is safe to use when drawing through `Frame::size`. * Prevent unsigned int overflow on large screens. ## v0.3.0 - 2018-11-04 ### Features * Add experimental test backend ## v0.3.0-beta.3 - 2018-09-24 ### Features * `show_cursor` is called when `Terminal` is dropped if the cursor is hidden. ## v0.3.0-beta.2 - 2018-09-23 ### Breaking Changes * Remove custom `termion` backends. This is motivated by the fact that `termion` structs are meant to be combined/wrapped to provide additional functionalities to the terminal (e.g AlternateScreen, Mouse support, ...). Thus providing exclusive types do not make a lot of sense and give a false hint that additional features cannot be used together. The recommended approach is now to create your own version of `stdout`: ```rust let stdout = io::stdout().into_raw_mode()?; let stdout = MouseTerminal::from(stdout); let stdout = AlternateScreen::from(stdout); ``` and then to create the corresponding `termion` backend: ```rust let backend = TermionBackend::new(stdout); ``` The resulting code is more verbose but it works with all combinations of additional `termion` features. ## v0.3.0-beta.1 - 2018-09-08 ### Breaking Changes * Replace `Item` by a generic and flexible `Text` that can be used in both `Paragraph` and `List` widgets. * Remove unecessary borrows on `Style`. ## v0.3.0-beta.0 - 2018-09-04 ### Features * Add a basic `Crossterm` backend ### Breaking Changes * Remove `Group` and introduce `Layout` in its place - `Terminal` is no longer required to compute a layout - `Size` has been renamed `Constraint` * Widgets are rendered on a `Frame` instead of a `Terminal` in order to avoid mixing `draw` and `render` calls * `draw` on `Terminal` expects a closure where the UI is built by rendering widgets on the given `Frame` * Update `Widget` trait - `draw` takes area by value - `render` takes a `Frame` instead of a `Terminal` * All widgets use the consumable builder pattern * `SelectableList` can have no selected item and the highlight symbol is hidden in this case * Remove markup langage inside `Paragraph`. `Paragraph` now expects an iterator of `Text` items ## v0.2.3 - 2018-06-09 ### Features * Add `start_corner` option for `List` * Add more text aligment options for `Paragraph` ## v0.2.2 - 2018-05-06 ### Features * `Terminal` implements `Debug` ### Breaking Changes * Use `FnOnce` instead of `FnMut` in Group::render ## v0.2.1 - 2018-04-01 ### Features * Add `AlternateScreenBackend` in `termion` backend * Add `TermionBackend::with_stdout` in order to let an user of the library provides its own termion struct * Add tests and documentation for `Buffer::pos_of` * Remove leading whitespaces when wrapping text ### Bug Fixes * Fix `debug_assert` in `Buffer::pos_of` * Pass the style of `SelectableList` to the underlying `List` * Fix missing character when wrapping text * Fix panic when specifying layout constraints ## v0.2.0 - 2017-12-26 ### Features * Add `MouseBackend` in `termion` backend to handle scroll and mouse events * Add generic `Item` for items in a `List` * Drop `log4rs` as a dev-dependencies in favor of `stderrlog` ### Breaking Changes * Rename `TermionBackend` to `RawBackend` (to distinguish it from the `MouseBackend`) * Generic parameters for `List` to allow passing iterators as items * Generic parameters for `Table` to allow using iterators as rows and header * Generic parameters for `Tabs` * Rename `border` bitflags to `Borders`