From 357ae7e251721f2e7fcb539de5e6fc60eaa30e29 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Sat, 30 Nov 2024 17:11:17 -0800 Subject: [PATCH] chore: move terminal types to ratatui-core (#1530) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- BREAKING-CHANGES.md | 23 +++++ README.md | 3 +- ratatui-core/src/lib.rs | 1 + {ratatui => ratatui-core}/src/terminal.rs | 6 -- .../src/terminal/frame.rs | 81 ++---------------- .../src/terminal/terminal.rs | 5 +- .../src/terminal/viewport.rs | 2 +- ratatui-crossterm/Cargo.toml | 3 +- ratatui-termion/Cargo.toml | 5 +- ratatui-termion/src/lib.rs | 2 +- ratatui/Cargo.toml | 7 +- ratatui/src/{terminal => }/init.rs | 3 +- ratatui/src/lib.rs | 18 ++-- ratatui/src/widgets.rs | 85 +++++++++++++++++++ 14 files changed, 144 insertions(+), 100 deletions(-) rename {ratatui => ratatui-core}/src/terminal.rs (87%) rename {ratatui => ratatui-core}/src/terminal/frame.rs (71%) rename {ratatui => ratatui-core}/src/terminal/terminal.rs (99%) rename {ratatui => ratatui-core}/src/terminal/viewport.rs (96%) rename ratatui/src/{terminal => }/init.rs (99%) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index a0834d10..cebd1c29 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -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` 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 diff --git a/README.md b/README.md index 531ca105..caaa625c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/ratatui-core/src/lib.rs b/ratatui-core/src/lib.rs index e900e11e..7c9a60cc 100644 --- a/ratatui-core/src/lib.rs +++ b/ratatui-core/src/lib.rs @@ -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; diff --git a/ratatui/src/terminal.rs b/ratatui-core/src/terminal.rs similarity index 87% rename from ratatui/src/terminal.rs rename to ratatui-core/src/terminal.rs index 1cd1adab..bd401079 100644 --- a/ratatui/src/terminal.rs +++ b/ratatui-core/src/terminal.rs @@ -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; diff --git a/ratatui/src/terminal/frame.rs b/ratatui-core/src/terminal/frame.rs similarity index 71% rename from ratatui/src/terminal/frame.rs rename to ratatui-core/src/terminal/frame.rs index 41e5f11f..72bedb49 100644 --- a/ratatui/src/terminal/frame.rs +++ b/ratatui-core/src/terminal/frame.rs @@ -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(&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(&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>(&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 }); diff --git a/ratatui/src/terminal/terminal.rs b/ratatui-core/src/terminal/terminal.rs similarity index 99% rename from ratatui/src/terminal/terminal.rs rename to ratatui-core/src/terminal/terminal.rs index 2174dc6b..ead211ab 100644 --- a/ratatui/src/terminal/terminal.rs +++ b/ratatui-core/src/terminal/terminal.rs @@ -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. diff --git a/ratatui/src/terminal/viewport.rs b/ratatui-core/src/terminal/viewport.rs similarity index 96% rename from ratatui/src/terminal/viewport.rs rename to ratatui-core/src/terminal/viewport.rs index 330fa6de..2ae95aba 100644 --- a/ratatui/src/terminal/viewport.rs +++ b/ratatui-core/src/terminal/viewport.rs @@ -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 diff --git a/ratatui-crossterm/Cargo.toml b/ratatui-crossterm/Cargo.toml index ba70a722..9425519b 100644 --- a/ratatui-crossterm/Cargo.toml +++ b/ratatui-crossterm/Cargo.toml @@ -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. diff --git a/ratatui-termion/Cargo.toml b/ratatui-termion/Cargo.toml index 3c4a2804..fdc3de1f 100644 --- a/ratatui-termion/Cargo.toml +++ b/ratatui-termion/Cargo.toml @@ -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 } diff --git a/ratatui-termion/src/lib.rs b/ratatui-termion/src/lib.rs index 68c8e4e1..d7324342 100644 --- a/ratatui-termion/src/lib.rs +++ b/ratatui-termion/src/lib.rs @@ -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 diff --git a/ratatui/Cargo.toml b/ratatui/Cargo.toml index cbab7e03..66bd09b8 100644 --- a/ratatui/Cargo.toml +++ b/ratatui/Cargo.toml @@ -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 } diff --git a/ratatui/src/terminal/init.rs b/ratatui/src/init.rs similarity index 99% rename from ratatui/src/terminal/init.rs rename to ratatui/src/init.rs index cad35b63..d94caf4f 100644 --- a/ratatui/src/terminal/init.rs +++ b/ratatui/src/init.rs @@ -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 diff --git a/ratatui/src/lib.rs b/ratatui/src/lib.rs index 955daf79..abeac52b 100644 --- a/ratatui/src/lib.rs +++ b/ratatui/src/lib.rs @@ -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; diff --git a/ratatui/src/widgets.rs b/ratatui/src/widgets.rs index ec5f0102..00959cd7 100644 --- a/ratatui/src/widgets.rs +++ b/ratatui/src/widgets.rs @@ -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(&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(&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(&mut self, widget: W, area: Rect) { + widget.render_ref(area, self.buffer_mut()); + } + + #[allow(clippy::needless_pass_by_value)] + fn render_stateful_widget_ref(&mut self, widget: W, area: Rect, state: &mut W::State) + where + W: StatefulWidgetRef, + { + widget.render_ref(area, self.buffer_mut(), state); + } +}