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)
- The `From` impls for backend types are now replaced with more specific traits
- `FrameExt` trait for `unstable-widget-ref` feature
- [v0.29.0](#v0290)
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
- Removed public fields from `Rect` iterators
@ -76,6 +77,28 @@ This is a quick summary of the sections below:
## 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
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
[Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
[FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
[`Frame`]: terminal::Frame
[`render_widget`]: terminal::Frame::render_widget
[`render_widget`]: Frame::render_widget
[`Widget`]: widgets::Widget
[`Layout`]: layout::Layout
[`Text`]: text::Text

View File

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

View File

@ -32,15 +32,9 @@
//! [`Buffer`]: crate::buffer::Buffer
mod frame;
#[cfg(feature = "crossterm")]
mod init;
mod terminal;
mod viewport;
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 viewport::Viewport;

View File

@ -1,7 +1,7 @@
use crate::{
buffer::Buffer,
layout::{Position, Rect},
widgets::{StatefulWidget, StatefulWidgetRef, Widget, WidgetRef},
widgets::{StatefulWidget, Widget},
};
/// 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.
///
/// [`Buffer`]: crate::buffer::Buffer
/// [`Terminal::draw`]: crate::Terminal::draw
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
#[derive(Debug, Hash)]
pub struct Frame<'a> {
/// 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`].
///
/// [`Terminal::draw`]: crate::Terminal::draw
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct CompletedFrame<'a> {
/// The buffer that was used to draw the last frame.
@ -96,32 +96,6 @@ impl Frame<'_> {
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`].
///
/// 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);
}
/// 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)
/// 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
/// stick with it.
///
/// [`Terminal::hide_cursor`]: crate::Terminal::hide_cursor
/// [`Terminal::show_cursor`]: crate::Terminal::show_cursor
/// [`Terminal::set_cursor_position`]: crate::Terminal::set_cursor_position
/// [`Terminal::hide_cursor`]: crate::terminal::Terminal::hide_cursor
/// [`Terminal::show_cursor`]: crate::terminal::Terminal::show_cursor
/// [`Terminal::set_cursor_position`]: crate::terminal::Terminal::set_cursor_position
pub fn set_cursor_position<P: Into<Position>>(&mut self, position: P) {
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
/// stick with it.
///
/// [`Terminal::hide_cursor`]: crate::Terminal::hide_cursor
/// [`Terminal::show_cursor`]: crate::Terminal::show_cursor
/// [`Terminal::set_cursor_position`]: crate::Terminal::set_cursor_position
/// [`Terminal::hide_cursor`]: crate::terminal::Terminal::hide_cursor
/// [`Terminal::show_cursor`]: crate::terminal::Terminal::show_cursor
/// [`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"]
pub fn set_cursor(&mut self, x: u16, y: u16) {
self.set_cursor_position(Position { x, y });

View File

@ -1,11 +1,10 @@
use std::io;
use ratatui_core::backend::{Backend, ClearType};
use crate::{
backend::{Backend, ClearType},
buffer::{Buffer, Cell},
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.

View File

@ -15,7 +15,7 @@ use crate::layout::Rect;
///
/// 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)]
pub enum Viewport {
/// The viewport is fullscreen

View File

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

View File

@ -21,9 +21,12 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = []
## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering.
scrolling-regions = ["ratatui-core/scrolling-regions"]
## Enables all unstable features.
unstable = ["unstable-backend-writer"]
## Enables getting access to backends' writer.
unstable-backend-writer = []
[dependencies]
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
/// [`IntoAlternateScreen::into_alternate_screen()`]: termion::screen::IntoAlternateScreen
/// [`Terminal`]: ratatui::terminal::Terminal
/// [`Terminal`]: ratatui_core::terminal::Terminal
/// [Termion]: https://docs.rs/termion
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct TermionBackend<W>

View File

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

View File

@ -1,5 +1,6 @@
use std::io::{self, stdout, Stdout};
use ratatui_core::terminal::{Terminal, TerminalOptions};
use ratatui_crossterm::{
crossterm::{
execute,
@ -8,8 +9,6 @@ use ratatui_crossterm::{
CrosstermBackend,
};
use crate::{terminal::TerminalOptions, Terminal};
/// A type alias for the default terminal type.
///
/// 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
//! [Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
//! [FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
//! [`Frame`]: terminal::Frame
//! [`render_widget`]: terminal::Frame::render_widget
//! [`render_widget`]: Frame::render_widget
//! [`Widget`]: widgets::Widget
//! [`Layout`]: layout::Layout
//! [`Text`]: text::Text
@ -329,6 +328,10 @@
/// re-export the `palette` crate so that users don't have to add it as a dependency
#[cfg(feature = "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
#[cfg(feature = "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
#[cfg(feature = "termwiz")]
pub use ratatui_termwiz::termwiz;
#[cfg(feature = "crossterm")]
pub use terminal::{
pub use crate::init::{
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.
pub mod backend {
@ -355,10 +358,9 @@ pub mod backend {
pub use ratatui_termwiz::{FromTermwiz, IntoTermwiz, TermwizBackend};
}
pub use ratatui_core::{buffer, layout};
pub mod prelude;
pub use ratatui_core::{style, symbols};
mod terminal;
pub use ratatui_core::text;
pub use ratatui_core::{style, symbols, text};
pub mod widgets;
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 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);
}
}