chore: move terminal types to ratatui-core (#1530)

- Move Terminal, TerminalOptions, ViewPort, CompletedFrame, Frame to
  ratatui-core crate
- Move render_widget_ref() and render_stateful_widget_ref() to extension
  trait (FrameExt) due as the Ref types are unstable and kept in the
  main lib instead of -core
- Fix rustdoc errors / feature config issues

BREAKING CHANGE: to call `Frame::render_widget_ref()` or
`Frame::render_stateful_widget_ref()` you now need to import the
FrameExt trait from `ratatui::widgets` and enable the
`unstable-widget-ref` feature.

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
Josh McKinney 2024-11-30 17:11:17 -08:00 committed by GitHub
parent 3b13240728
commit 357ae7e251
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 144 additions and 100 deletions

View File

@ -12,6 +12,7 @@ This is a quick summary of the sections below:
- [Unreleased](#unreleased) - [Unreleased](#unreleased)
- The `From` impls for backend types are now replaced with more specific traits - The `From` impls for backend types are now replaced with more specific traits
- `FrameExt` trait for `unstable-widget-ref` feature
- [v0.29.0](#v0290) - [v0.29.0](#v0290)
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const - `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
- Removed public fields from `Rect` iterators - Removed public fields from `Rect` iterators
@ -76,6 +77,28 @@ This is a quick summary of the sections below:
## Unreleased (0.30.0) ## Unreleased (0.30.0)
### `FrameExt` trait for `unstable-widget-ref` feature ([#1530])
[#1530]: https://github.com/ratatui/ratatui/pull/1530
To call `Frame::render_widget_ref()` or `Frame::render_stateful_widget_ref()` you now need to:
1. Import the `FrameExt` trait from `ratatui::widgets`.
2. Enable the `unstable-widget-ref` feature.
For example:
```rust
use ratatui::{
layout::Rect,
widgets::{Block, FrameExt},
};
let block = Block::new();
let area = Rect::new(0, 0, 5, 5);
frame.render_widget_ref(&block, area);
```
### `WidgetRef` no longer has a blanket implementation of Widget ### `WidgetRef` no longer has a blanket implementation of Widget
Previously there was a blanket implementation of Widget for WidgetRef. This has been reversed to Previously there was a blanket implementation of Widget for WidgetRef. This has been reversed to

View File

@ -304,8 +304,7 @@ fn draw(frame: &mut Frame) {
[Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md [Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md
[Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md [Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
[FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20 [FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
[`Frame`]: terminal::Frame [`render_widget`]: Frame::render_widget
[`render_widget`]: terminal::Frame::render_widget
[`Widget`]: widgets::Widget [`Widget`]: widgets::Widget
[`Layout`]: layout::Layout [`Layout`]: layout::Layout
[`Text`]: text::Text [`Text`]: text::Text

View File

@ -43,5 +43,6 @@ pub mod buffer;
pub mod layout; pub mod layout;
pub mod style; pub mod style;
pub mod symbols; pub mod symbols;
pub mod terminal;
pub mod text; pub mod text;
pub mod widgets; pub mod widgets;

View File

@ -32,15 +32,9 @@
//! [`Buffer`]: crate::buffer::Buffer //! [`Buffer`]: crate::buffer::Buffer
mod frame; mod frame;
#[cfg(feature = "crossterm")]
mod init;
mod terminal; mod terminal;
mod viewport; mod viewport;
pub use frame::{CompletedFrame, Frame}; pub use frame::{CompletedFrame, Frame};
#[cfg(feature = "crossterm")]
pub use init::{
init, init_with_options, restore, try_init, try_init_with_options, try_restore, DefaultTerminal,
};
pub use terminal::{Options as TerminalOptions, Terminal}; pub use terminal::{Options as TerminalOptions, Terminal};
pub use viewport::Viewport; pub use viewport::Viewport;

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
buffer::Buffer, buffer::Buffer,
layout::{Position, Rect}, layout::{Position, Rect},
widgets::{StatefulWidget, StatefulWidgetRef, Widget, WidgetRef}, widgets::{StatefulWidget, Widget},
}; };
/// A consistent view into the terminal state for rendering a single frame. /// A consistent view into the terminal state for rendering a single frame.
@ -14,7 +14,7 @@ use crate::{
/// to the terminal. This avoids drawing redundant cells. /// to the terminal. This avoids drawing redundant cells.
/// ///
/// [`Buffer`]: crate::buffer::Buffer /// [`Buffer`]: crate::buffer::Buffer
/// [`Terminal::draw`]: crate::Terminal::draw /// [`Terminal::draw`]: crate::terminal::Terminal::draw
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct Frame<'a> { pub struct Frame<'a> {
/// Where should the cursor be after drawing this frame? /// Where should the cursor be after drawing this frame?
@ -37,7 +37,7 @@ pub struct Frame<'a> {
/// [`Terminal::draw`] call have been applied. Therefore, it is only valid until the next call to /// [`Terminal::draw`] call have been applied. Therefore, it is only valid until the next call to
/// [`Terminal::draw`]. /// [`Terminal::draw`].
/// ///
/// [`Terminal::draw`]: crate::Terminal::draw /// [`Terminal::draw`]: crate::terminal::Terminal::draw
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct CompletedFrame<'a> { pub struct CompletedFrame<'a> {
/// The buffer that was used to draw the last frame. /// The buffer that was used to draw the last frame.
@ -96,32 +96,6 @@ impl Frame<'_> {
widget.render(area, self.buffer); widget.render(area, self.buffer);
} }
/// Render a [`WidgetRef`] to the current buffer using [`WidgetRef::render_ref`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
/// frame (which can be obtained using [`Layout`] to split the total area).
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "unstable-widget-ref")] {
/// # use ratatui::{backend::TestBackend, Terminal};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// use ratatui::{layout::Rect, widgets::Block};
///
/// let block = Block::new();
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_widget_ref(&block, area);
/// # }
/// ```
#[allow(clippy::needless_pass_by_value)]
#[instability::unstable(feature = "widget-ref")]
pub fn render_widget_ref<W: WidgetRef>(&mut self, widget: W, area: Rect) {
widget.render_ref(area, self.buffer);
}
/// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`]. /// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`].
/// ///
/// Usually the area argument is the size of the current frame or a sub-area of the current /// Usually the area argument is the size of the current frame or a sub-area of the current
@ -156,43 +130,6 @@ impl Frame<'_> {
widget.render(area, self.buffer, state); widget.render(area, self.buffer, state);
} }
/// Render a [`StatefulWidgetRef`] to the current buffer using
/// [`StatefulWidgetRef::render_ref`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
/// frame (which can be obtained using [`Layout`] to split the total area).
///
/// The last argument should be an instance of the [`StatefulWidgetRef::State`] associated to
/// the given [`StatefulWidgetRef`].
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "unstable-widget-ref")] {
/// # use ratatui::{backend::TestBackend, Terminal};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// use ratatui::{
/// layout::Rect,
/// widgets::{List, ListItem, ListState},
/// };
///
/// let mut state = ListState::default().with_selected(Some(1));
/// let list = List::new(vec![ListItem::new("Item 1"), ListItem::new("Item 2")]);
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_stateful_widget_ref(&list, area, &mut state);
/// # }
/// ```
#[allow(clippy::needless_pass_by_value)]
#[instability::unstable(feature = "widget-ref")]
pub fn render_stateful_widget_ref<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
where
W: StatefulWidgetRef,
{
widget.render_ref(area, self.buffer, state);
}
/// After drawing this frame, make the cursor visible and put it at the specified (x, y) /// After drawing this frame, make the cursor visible and put it at the specified (x, y)
/// coordinates. If this method is not called, the cursor will be hidden. /// coordinates. If this method is not called, the cursor will be hidden.
/// ///
@ -200,9 +137,9 @@ impl Frame<'_> {
/// [`Terminal::show_cursor`], and [`Terminal::set_cursor_position`]. Pick one of the APIs and /// [`Terminal::show_cursor`], and [`Terminal::set_cursor_position`]. Pick one of the APIs and
/// stick with it. /// stick with it.
/// ///
/// [`Terminal::hide_cursor`]: crate::Terminal::hide_cursor /// [`Terminal::hide_cursor`]: crate::terminal::Terminal::hide_cursor
/// [`Terminal::show_cursor`]: crate::Terminal::show_cursor /// [`Terminal::show_cursor`]: crate::terminal::Terminal::show_cursor
/// [`Terminal::set_cursor_position`]: crate::Terminal::set_cursor_position /// [`Terminal::set_cursor_position`]: crate::terminal::Terminal::set_cursor_position
pub fn set_cursor_position<P: Into<Position>>(&mut self, position: P) { pub fn set_cursor_position<P: Into<Position>>(&mut self, position: P) {
self.cursor_position = Some(position.into()); self.cursor_position = Some(position.into());
} }
@ -214,9 +151,9 @@ impl Frame<'_> {
/// [`Terminal::show_cursor`], and [`Terminal::set_cursor_position`]. Pick one of the APIs and /// [`Terminal::show_cursor`], and [`Terminal::set_cursor_position`]. Pick one of the APIs and
/// stick with it. /// stick with it.
/// ///
/// [`Terminal::hide_cursor`]: crate::Terminal::hide_cursor /// [`Terminal::hide_cursor`]: crate::terminal::Terminal::hide_cursor
/// [`Terminal::show_cursor`]: crate::Terminal::show_cursor /// [`Terminal::show_cursor`]: crate::terminal::Terminal::show_cursor
/// [`Terminal::set_cursor_position`]: crate::Terminal::set_cursor_position /// [`Terminal::set_cursor_position`]: crate::terminal::Terminal::set_cursor_position
#[deprecated = "the method set_cursor_position indicates more clearly what about the cursor to set"] #[deprecated = "the method set_cursor_position indicates more clearly what about the cursor to set"]
pub fn set_cursor(&mut self, x: u16, y: u16) { pub fn set_cursor(&mut self, x: u16, y: u16) {
self.set_cursor_position(Position { x, y }); self.set_cursor_position(Position { x, y });

View File

@ -1,11 +1,10 @@
use std::io; use std::io;
use ratatui_core::backend::{Backend, ClearType};
use crate::{ use crate::{
backend::{Backend, ClearType},
buffer::{Buffer, Cell}, buffer::{Buffer, Cell},
layout::{Position, Rect, Size}, layout::{Position, Rect, Size},
CompletedFrame, Frame, TerminalOptions, Viewport, terminal::{CompletedFrame, Frame, TerminalOptions, Viewport},
}; };
/// An interface to interact and draw [`Frame`]s on the user's terminal. /// An interface to interact and draw [`Frame`]s on the user's terminal.

View File

@ -15,7 +15,7 @@ use crate::layout::Rect;
/// ///
/// See [`Terminal::with_options`] for more information. /// See [`Terminal::with_options`] for more information.
/// ///
/// [`Terminal::with_options`]: crate::Terminal::with_options /// [`Terminal::with_options`]: crate::terminal::Terminal::with_options
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub enum Viewport { pub enum Viewport {
/// The viewport is fullscreen /// The viewport is fullscreen

View File

@ -18,8 +18,7 @@ rust-version.workspace = true
default = ["underline-color"] default = ["underline-color"]
## enables the backend code that sets the underline color. ## enables the backend code that sets the underline color.
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend, ## Underline color is not supported on Windows 7.
## and is not supported on Windows 7.
underline-color = ["ratatui-core/underline-color"] underline-color = ["ratatui-core/underline-color"]
## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering. ## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering.

View File

@ -21,9 +21,12 @@ rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
default = [] default = []
## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering. ## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering.
scrolling-regions = ["ratatui-core/scrolling-regions"] scrolling-regions = ["ratatui-core/scrolling-regions"]
## Enables all unstable features.
unstable = ["unstable-backend-writer"]
## Enables getting access to backends' writer.
unstable-backend-writer = []
[dependencies] [dependencies]
document-features = { workspace = true, optional = true } document-features = { workspace = true, optional = true }

View File

@ -72,7 +72,7 @@ use termion::{color as tcolor, color::Color as _, style as tstyle};
/// ///
/// [`IntoRawMode::into_raw_mode()`]: termion::raw::IntoRawMode /// [`IntoRawMode::into_raw_mode()`]: termion::raw::IntoRawMode
/// [`IntoAlternateScreen::into_alternate_screen()`]: termion::screen::IntoAlternateScreen /// [`IntoAlternateScreen::into_alternate_screen()`]: termion::screen::IntoAlternateScreen
/// [`Terminal`]: ratatui::terminal::Terminal /// [`Terminal`]: ratatui_core::terminal::Terminal
/// [Termion]: https://docs.rs/termion /// [Termion]: https://docs.rs/termion
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct TermionBackend<W> pub struct TermionBackend<W>

View File

@ -89,8 +89,11 @@ unstable-rendered-line-info = ["ratatui-widgets/unstable-rendered-line-info"]
## [`StatefulWidgetRef`]: widgets::StatefulWidgetRef ## [`StatefulWidgetRef`]: widgets::StatefulWidgetRef
unstable-widget-ref = [] unstable-widget-ref = []
## Enables getting access to backends' writers. Only the Crossterm backend currently supports this. ## Enables getting access to backends' writers.
unstable-backend-writer = ["ratatui-crossterm?/unstable-backend-writer"] unstable-backend-writer = [
"ratatui-crossterm?/unstable-backend-writer",
"ratatui-termion?/unstable-backend-writer",
]
[dependencies] [dependencies]
document-features = { workspace = true, optional = true } document-features = { workspace = true, optional = true }

View File

@ -1,5 +1,6 @@
use std::io::{self, stdout, Stdout}; use std::io::{self, stdout, Stdout};
use ratatui_core::terminal::{Terminal, TerminalOptions};
use ratatui_crossterm::{ use ratatui_crossterm::{
crossterm::{ crossterm::{
execute, execute,
@ -8,8 +9,6 @@ use ratatui_crossterm::{
CrosstermBackend, CrosstermBackend,
}; };
use crate::{terminal::TerminalOptions, Terminal};
/// A type alias for the default terminal type. /// A type alias for the default terminal type.
/// ///
/// This is a [`Terminal`] using the [`CrosstermBackend`] which writes to [`Stdout`]. This is a /// This is a [`Terminal`] using the [`CrosstermBackend`] which writes to [`Stdout`]. This is a

View File

@ -290,8 +290,7 @@
//! [Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md //! [Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md
//! [Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md //! [Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
//! [FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20 //! [FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
//! [`Frame`]: terminal::Frame //! [`render_widget`]: Frame::render_widget
//! [`render_widget`]: terminal::Frame::render_widget
//! [`Widget`]: widgets::Widget //! [`Widget`]: widgets::Widget
//! [`Layout`]: layout::Layout //! [`Layout`]: layout::Layout
//! [`Text`]: text::Text //! [`Text`]: text::Text
@ -329,6 +328,10 @@
/// re-export the `palette` crate so that users don't have to add it as a dependency /// re-export the `palette` crate so that users don't have to add it as a dependency
#[cfg(feature = "palette")] #[cfg(feature = "palette")]
pub use palette; pub use palette;
pub use ratatui_core::{
buffer, layout,
terminal::{CompletedFrame, Frame, Terminal, TerminalOptions, Viewport},
};
/// re-export the `crossterm` crate so that users don't have to add it as a dependency /// re-export the `crossterm` crate so that users don't have to add it as a dependency
#[cfg(feature = "crossterm")] #[cfg(feature = "crossterm")]
pub use ratatui_crossterm::crossterm; pub use ratatui_crossterm::crossterm;
@ -338,11 +341,11 @@ pub use ratatui_termion::termion;
/// re-export the `termwiz` crate so that users don't have to add it as a dependency /// re-export the `termwiz` crate so that users don't have to add it as a dependency
#[cfg(feature = "termwiz")] #[cfg(feature = "termwiz")]
pub use ratatui_termwiz::termwiz; pub use ratatui_termwiz::termwiz;
#[cfg(feature = "crossterm")] #[cfg(feature = "crossterm")]
pub use terminal::{ pub use crate::init::{
init, init_with_options, restore, try_init, try_init_with_options, try_restore, DefaultTerminal, init, init_with_options, restore, try_init, try_init_with_options, try_restore, DefaultTerminal,
}; };
pub use terminal::{CompletedFrame, Frame, Terminal, TerminalOptions, Viewport};
/// Re-exports for the backend implementations. /// Re-exports for the backend implementations.
pub mod backend { pub mod backend {
@ -355,10 +358,9 @@ pub mod backend {
pub use ratatui_termwiz::{FromTermwiz, IntoTermwiz, TermwizBackend}; pub use ratatui_termwiz::{FromTermwiz, IntoTermwiz, TermwizBackend};
} }
pub use ratatui_core::{buffer, layout};
pub mod prelude; pub mod prelude;
pub use ratatui_core::{style, symbols}; pub use ratatui_core::{style, symbols, text};
mod terminal;
pub use ratatui_core::text;
pub mod widgets; pub mod widgets;
pub use ratatui_widgets::border; pub use ratatui_widgets::border;
#[cfg(feature = "crossterm")]
mod init;

View File

@ -54,3 +54,88 @@ pub use {stateful_widget_ref::StatefulWidgetRef, widget_ref::WidgetRef};
mod stateful_widget_ref; mod stateful_widget_ref;
mod widget_ref; mod widget_ref;
use ratatui_core::layout::Rect;
/// Extension trait for [`Frame`] that provides methods to render [`WidgetRef`] and
/// [`StatefulWidgetRef`] to the current buffer.
#[instability::unstable(feature = "widget-ref")]
pub trait FrameExt {
/// Render a [`WidgetRef`] to the current buffer using [`WidgetRef::render_ref`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
/// frame (which can be obtained using [`Layout`] to split the total area).
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "unstable-widget-ref")] {
/// # use ratatui::{backend::TestBackend, Terminal};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// use ratatui::{
/// layout::Rect,
/// widgets::{Block, FrameExt},
/// };
///
/// let block = Block::new();
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_widget_ref(&block, area);
/// # }
/// ```
///
/// [`Layout`]: crate::layout::Layout
#[allow(clippy::needless_pass_by_value)]
fn render_widget_ref<W: WidgetRef>(&mut self, widget: W, area: Rect);
/// Render a [`StatefulWidgetRef`] to the current buffer using
/// [`StatefulWidgetRef::render_ref`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
/// frame (which can be obtained using [`Layout`] to split the total area).
///
/// The last argument should be an instance of the [`StatefulWidgetRef::State`] associated to
/// the given [`StatefulWidgetRef`].
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "unstable-widget-ref")] {
/// # use ratatui::{backend::TestBackend, Terminal};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// use ratatui::{
/// layout::Rect,
/// widgets::{FrameExt, List, ListItem, ListState},
/// };
///
/// let mut state = ListState::default().with_selected(Some(1));
/// let list = List::new(vec![ListItem::new("Item 1"), ListItem::new("Item 2")]);
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_stateful_widget_ref(&list, area, &mut state);
/// # }
/// ```
/// [`Layout`]: crate::layout::Layout
#[allow(clippy::needless_pass_by_value)]
fn render_stateful_widget_ref<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
where
W: StatefulWidgetRef;
}
#[cfg(feature = "unstable-widget-ref")]
impl FrameExt for ratatui_core::terminal::Frame<'_> {
#[allow(clippy::needless_pass_by_value)]
fn render_widget_ref<W: WidgetRef>(&mut self, widget: W, area: Rect) {
widget.render_ref(area, self.buffer_mut());
}
#[allow(clippy::needless_pass_by_value)]
fn render_stateful_widget_ref<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
where
W: StatefulWidgetRef,
{
widget.render_ref(area, self.buffer_mut(), state);
}
}