diff --git a/astro.config.mjs b/astro.config.mjs index a9afc66cb..d64e8a431 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -257,6 +257,7 @@ export default defineConfig({ collapsed: true, items: [ { label: "Highlights", link: "/highlights/" }, + { label: "v0.26", link: "/highlights/v026/" }, { label: "v0.25", link: "/highlights/v025/" }, { label: "v0.24", link: "/highlights/v024/" }, { label: "v0.23", link: "/highlights/v023/" }, diff --git a/src/content/docs/highlights/constraints.gif b/src/content/docs/highlights/constraints.gif new file mode 100644 index 000000000..5bc981174 --- /dev/null +++ b/src/content/docs/highlights/constraints.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cdcf980dbb665bbb3c484c6ed24f1b56b14515b493b7c0a62e29e4a47c07684 +size 200782 diff --git a/src/content/docs/highlights/index.md b/src/content/docs/highlights/index.md index 4ceade2ab..1150fc310 100644 --- a/src/content/docs/highlights/index.md +++ b/src/content/docs/highlights/index.md @@ -5,6 +5,7 @@ title: Highlights This section highlights the main changes in each major release of Ratatui (from v0.21.0 through the latest version). +- [v0.26](./v026/) - [v0.25](./v025/) - [v0.24](./v024/) - [v0.23](./v023/) diff --git a/src/content/docs/highlights/v0.26.md b/src/content/docs/highlights/v0.26.md new file mode 100644 index 000000000..0598a713d --- /dev/null +++ b/src/content/docs/highlights/v0.26.md @@ -0,0 +1,481 @@ +--- +title: v0.26.0 +--- + + + +⚠️ See the [breaking changes](https://github.com/ratatui-org/ratatui/blob/main/BREAKING-CHANGES.md) +for this release. + +## FOSDEM πŸ“’ + +At the time this release is published, one of our maintainers +([Orhun ParmaksΔ±z](https://github.com/orhun)) will be giving an introductory talk about Ratatui at +[FOSDEM](https://fosdem.org/2024/)! The talk will be also recorded and streamed live. + +See the event details +[here](https://fosdem.org/2024/schedule/event/fosdem-2024-1934-introducing-ratatui-a-rust-library-to-cook-up-terminal-user-interfaces). + +If you are around in person, don't miss the chance to get some Ratatui stickers! + +## Demo: Destroy Mode πŸ’₯ + +We have a brand new demo which has a destroy mode! (Made for celebrating the 1000th commit of +Ratatui) + +![Destroy Demo2](https://github.com/ratatui-org/ratatui/blob/1d39444e3dea6f309cf9035be2417ac711c1abc9/examples/demo2-destroy.gif?raw=true) + +To run it: + +```shell +cargo run --example demo2 --features="crossterm widget-calendar" +``` + +Press `d` to activate destroy mode and enjoy! + +## Ref Widget Implementation 🧩 + +Many widgets can now be rendered without changing their state. + +We implemented `WidgetRef` trait for references to widgets and changed their implementations to be +immutable. This allows us to render widgets without consuming them by passing a ref to the widget to +`Frame::render_widget()`. It also allows boxed widgets to be rendered. + +Note: this trait is gated behind a feature flag `unstable-widget-ref`. The approach we take might +change for this (as there are approaches that would allow the code below to just use `Widget` +instead of `WidgetRef` + +```rust +// this might be stored in a struct +let paragraph = Paragraph::new("Hello world!"); + +let [left, right] = area.split(&Layout::horizontal([20, 20])); +frame.render_widget(¶graph, left); +frame.render_widget(¶graph, right); // we can reuse the widget + +let widgets: Vec> = vec![Box::new(Line::raw("hello"), Span::raw("world"))]; +for widget in widgets { + widget.render_ref(area, &mut buf); +} +``` + +## Layout: `flex` ✨ + +We now support a new way to space the elements in a `Layout`: Flex! We added a `Flex` enum loosely +based on [flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/): + +- `Flex::Start` (new default) +- `Flex::Center` +- `Flex::End` +- `Flex::SpaceAround` +- `Flex::SpaceBetween` +- `Flex::Legacy` (old default) + +In addition to changing the default to `Flex::Start`, we have made a couple of changes to the +constraints. + +1. `Min(v)` grows to allocate excess space in all `Flex` modes instead of shrinking (except in + `Flex::Legacy` where it retains old behavior). +2. `Fill(1)` grows to allocate excess space, growing equally with `Min(v)`. + +While is a breaking change to the behavior of constraints, most users should see identical layouts +with the new `Flex::Start`, especially if `Min()` is one of the constraints. However, you want the +old behavior, you can use `Flex::Legacy`: + +```rust +Layout::vertical([Length(25), Length(25)]).flex(Flex::Legacy) +``` + +We have also removed the unstable feature `SegmentSize`. + +Check out the [pull request](https://github.com/ratatui-org/ratatui/issues/881) for the motivation +behind this feature and more information. + +We have also built a [constraint-explorer] TUI that will allow you to compare how constraints behave +in different `Flex` modes. Check out the +[pull request](https://github.com/ratatui-org/ratatui/pull/893) for a video demo of the +`constraint-explorer`. + +[constraint-explorer](https://github.com/ratatui-org/ratatui/blob/f8367fdfdd1da0ae98705a0b23fc88d156425f4c/examples/constraint-explorer.rs) + +## Color Palettes 🎨 + +There are two brand new colors palettes ready to use, Material and Tailwind. + +The `ratatui::style::palette::material` module contains the Google 2014 Material Design palette. + +```rust +use ratatui::style::palette::material::BLUE_GRAY; +Line::styled("Hello", BLUE_GRAY.c500); +``` + +The `ratatui::style::palette::tailwind` module contains the default Tailwind color palette. This is +useful for styling components with colors that match the Tailwind color palette. + +```rust +use ratatui::style::palette::tailwind::SLATE; +Line::styled("Hello", SLATE.c500); +``` + +See and + for more information.. + +## Alignment Convenience Functions πŸ—οΈ + +We added the following alignment convenience functions for `Line`, `Paragraph` and `Text`: + +- `Line::from("align on left").left_aligned();` +- `Line::from("centered!").centered();` +- `Line::from("align on right").right_aligned();` + +Same applies for `Paragraph` and `Text` e.g. `Paragraph::new("Hello, world!").centered()`. + +`Span` on the other hand has the following new methods: + +```rust +let span = Span::styled("Test Content", Style::new().green().italic()); + +// convert span to left-aligned line +let line = span.to_left_aligned_line(); + +// convert span to right-aligned line +let line = span.to_right_aligned_line(); + +// convert span to center-aligned line +let line = span.to_center_aligned_line(); +``` + +## Padding: New Constructors πŸ—οΈ + +`Padding` has new constructors: + +- `Padding::proportional(4);`: make horizontal and vertical padding seem equal +- `Padding::symmetric(5, 6);`: defines left and right padding +- `Padding::left(3);`: defines left padding +- `Padding::right(3);`: defines right padding +- `Padding::top(3);`: defines top padding +- `Padding::bottom(3);`: defines bottom padding + +## Block: `bordered` 🧱 + +`Block` has a new constructor method named `bordered` for avoiding creating a block with no borders +and setting `Borders::ALL`. + +So you can simplify your code as follows: + +```diff +- Block::default().borders(Borders::ALL); ++ Block::bordered(); +``` + +## Color: New Constructors πŸ—οΈ + +`Color` can be constructed from `u32` values now. The format is `0x00RRGGBB`: + +```rust +let white = Color::from_u32(0x00FFFFFF); +let black = Color::from_u32(0x00000000); +``` + +We also added `from_hsl` method for constructing `Color::Rgb` values. + +```rust +let color: Color = Color::from_hsl(360.0, 100.0, 100.0); +assert_eq!(color, Color::Rgb(255, 255, 255)); + +let color: Color = Color::from_hsl(0.0, 0.0, 0.0); +assert_eq!(color, Color::Rgb(0, 0, 0)); +``` + +> HSL stands for Hue (0-360 deg), Saturation (0-100%), and Lightness (0-100%) and working with HSL +> the values can be more intuitive. For example, if you want to make a red color more orange, you +> can change the Hue closer toward yellow on the color wheel (i.e. increase the Hue). + +## Layout: Increase Cache Size πŸ“ˆ + +We increase the default cache size of layout from 16 to 500. + +> This is a somewhat arbitrary size for the layout cache based on adding the columns and rows on my +> laptop's terminal (171+51 = 222) and doubling it for good measure and then adding a bit more to +> make it a round number. This gives enough entries to store a layout for every row and every +> column, twice over, which should be enough for most apps. + +For those that need more, the cache size can be set with `Layout::init_cache()`. + +See the relevant discussion in [this issue](https://github.com/ratatui-org/ratatui/issues/820). + +## Layout: Horizontal and Vertical Constructors πŸ—οΈ + +The `Layout` now allows to create a vertical or horizontal layout with default values with the +following constructors: + +```rust +let layout = Layout::vertical([Constraint::Length(10), Constraint::Min(5)]); +let layout = Layout::horizontal([Constraint::Length(10), Constraint::Min(5)]); +``` + +## Layout: Accept Constraints πŸ“ + +The Layout constructors now accept any type that implements `Into` instead of just +`AsRef`. This is useful when you want to specify a fixed size for a layout, but don't +want to explicitly create a Constraint::Length yourself. + +```rust +Layout::new(Direction::Vertical, [1, 2, 3]); +Layout::horizontal([1, 2, 3]); +Layout::vertical([1, 2, 3]); +Layout::default().constraints([1, 2, 3]); +``` + +## Layout: `spacing` πŸ“ + +Spacing can now be added between the items of a layout. + +```rust +let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(2); +``` + +## Rect: `contains` πŸ”² + +If you want to perform hit tests, this new method is for you. (e.g. did the user click in an area) + +```rust +Rect::new(1, 2, 3, 4).contains(Position { x: 1, y: 2 }) // true +``` + +We also added the `Position` struct for storing the x and y coordinates (columns and rows). + +## Rect: `clamp` 🦞 + +There is a new useful method when you want to be able to dynamically move a rectangle around, but +keep it constrained to a certain area. + +For example, this can be used to implement a draggable window that can be moved around, but not +outside the terminal window. + +```rust +let window_area = Rect::new(state.x, state.y, 20, 20).clamp(area); +state.x = rect.x; +state.y = rect.y; +``` + +## Layout: `areas` and `spacers` methods 🍌 + +Now you can split a `Rect` into multiple sub-`Rect`s in a more concise way: + +```rust +use Constraint::*; +let layout = Layout::vertical([Length(1), Min(0)]); +let [top, main] layout.areas(); +let [above, inbetwee, below] = layout.spacers(); +``` + +## Rect: Rows/Colums Iterators πŸ”² + +This enables iterating over rows and columns of a Rect this simplifies looping over cells. + +```rust +let area = Rect::new(0, 0, 3, 2); +let rows: Vec = area.rows().collect(); +let columns: Vec = area.columns().collect(); +``` + +## Table: Accept Constraints πŸ“Š + +Table constructors now accept any type that implements `Into` instead of just +`AsRef`. This is useful when you want to specify a fixed size for a table columns, but +don't want to explicitly create a Constraint::Length yourself. + +```rust +Table::new(rows, [1,2,3]) +Table::default().widths([1,2,3]) +``` + +## Table: Accepts Iterator πŸ“Š + +Previously, `Table::new()` accepted `IntoIterator>`. The argument change to +`IntoIterator>>`, This allows more flexible types from calling scopes, though it +can some break type inference in the calling scope for empty containers. + +```diff +- let table = Table::new(vec![], widths); +// becomes ++ let table = Table::default().widths(widths); +``` + +This also means that any iterator whose item is convertible into `Row` can now be collected into a +`Table`. + +## Table: Accept Text as highlight symbol πŸ“Š + +You can now use multi-line symbols for highlighting items in a table. + +```rust +let table = Table::new(rows, widths) + .highlight_symbol(Text::from(vec![ + "".into(), + " β–ˆ ".into(), + " β–ˆ ".into(), + "".into(), + ])); +``` + +
+See the demo + +![Made with VHS](https://vhs.charm.sh/vhs-6z75EkofPu13czwIq1ZhYw.gif) + +
+ +## Table: `footer` πŸ“Š + +`Table` now has a `footer` method for setting the rows that will be displayed at the bottom. + +```rust +let footer = Row::new(vec![ + Cell::from("Footer Cell 1"), + Cell::from("Footer Cell 2"), +]); +let table = Table::default().footer(footer); +``` + +Along with that, there is a new `top_margin` method of `Row`: + +```rust +let row = Row::default().top_margin(1); +``` + +## Widget Implementation 🧩 + +`Line` and `Span` now implements `Widget` which means it can be used as a child of other widgets. + +You can also use `Line::render()` to render it rather than calling `buffer.set_line()`. + +```rust +frame.render_widget(Line::raw("Hello, world!"), area); +// or +Line::raw("Hello, world!").render(frame, area); +``` + +Same applies to `Span` and you can use `Span::render()` to render it rather than calling +`buffer.set_span()`. + +## Line: `styled` 🎨 + +Previously the style of a `Line` was stored in the `Span`s that make up the line. Now the `Line` +itself has a `style` field, which can be set with the `Line::styled` method. + +```rust +let style = Style::new().yellow(); +let content = "Hello, world!"; +let line = Line::styled(content, style); +``` + +Any code that creates `Line`s using the struct initializer instead of constructors will fail to +compile due to the added field. This can be easily fixed by adding `..Default::default()` to the +field list or by using a constructor method (`Line::styled()`, `Line::raw()`) or conversion method +(`Line::from()`). + +```diff + let line = Line { + spans: vec!["".into()], + alignment: Alignment::Left, ++ ..Default::default() + }; +``` + +## Style: Accept Into 🎨 + +All style related methods now accept `S: Into