mirror of
https://github.com/ratatui/ratatui.git
synced 2025-09-29 05:51:57 +00:00

Many widgets can be rendered without changing their state. This commit implements The `Widget` trait for references to widgets and changes their implementations to be immutable. This allows us to render widgets without consuming them by passing a ref to the widget when calling `Frame::render_widget()`. ```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 ``` Implemented for all widgets except BarChart (which has an implementation that modifies the internal state and requires a rewrite to fix. Other widgets will be implemented in follow up commits. Fixes: https://github.com/ratatui-org/ratatui/discussions/164 Replaces PRs: https://github.com/ratatui-org/ratatui/pull/122 and https://github.com/ratatui-org/ratatui/pull/16 Enables: https://github.com/ratatui-org/ratatui/issues/132 Validated as a viable working solution by: https://github.com/ratatui-org/ratatui/pull/836
201 lines
5.6 KiB
Rust
201 lines
5.6 KiB
Rust
use crate::prelude::*;
|
|
|
|
/// A [`Cell`] contains the [`Text`] to be displayed in a [`Row`] of a [`Table`].
|
|
///
|
|
/// You can apply a [`Style`] to the [`Cell`] using [`Cell::style`]. This will set the style for the
|
|
/// entire area of the cell. Any [`Style`] set on the [`Text`] content will be combined with the
|
|
/// [`Style`] of the [`Cell`] by adding the [`Style`] of the [`Text`] content to the [`Style`] of
|
|
/// the [`Cell`]. Styles set on the text content will only affect the content.
|
|
///
|
|
/// You can use [`Text::alignment`] when creating a cell to align its content.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// You can create a `Cell` from anything that can be converted to a [`Text`].
|
|
///
|
|
/// ```rust
|
|
/// use std::borrow::Cow;
|
|
///
|
|
/// use ratatui::{prelude::*, widgets::*};
|
|
///
|
|
/// Cell::from("simple string");
|
|
/// Cell::from(Span::from("span"));
|
|
/// Cell::from(Line::from(vec![
|
|
/// Span::raw("a vec of "),
|
|
/// Span::styled("spans", Style::default().add_modifier(Modifier::BOLD)),
|
|
/// ]));
|
|
/// Cell::from(Text::from("a text"));
|
|
/// Cell::from(Text::from(Cow::Borrowed("hello")));
|
|
/// ```
|
|
///
|
|
/// `Cell` implements [`Styled`] which means you can use style shorthands from the [`Stylize`] trait
|
|
/// to set the style of the cell concisely.
|
|
///
|
|
/// ```rust
|
|
/// use ratatui::{prelude::*, widgets::*};
|
|
/// Cell::new("Cell 1").red().italic();
|
|
/// ```
|
|
///
|
|
/// [`Row`]: super::Row
|
|
/// [`Table`]: super::Table
|
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
|
pub struct Cell<'a> {
|
|
content: Text<'a>,
|
|
style: Style,
|
|
}
|
|
|
|
impl<'a> Cell<'a> {
|
|
/// Creates a new [`Cell`]
|
|
///
|
|
/// The `content` parameter accepts any value that can be converted into a [`Text`].
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// # use ratatui::{prelude::*, widgets::*};
|
|
/// Cell::new("simple string");
|
|
/// Cell::new(Span::from("span"));
|
|
/// Cell::new(Line::from(vec![
|
|
/// Span::raw("a vec of "),
|
|
/// Span::styled("spans", Style::default().add_modifier(Modifier::BOLD)),
|
|
/// ]));
|
|
/// Cell::new(Text::from("a text"));
|
|
/// ```
|
|
pub fn new<T>(content: T) -> Self
|
|
where
|
|
T: Into<Text<'a>>,
|
|
{
|
|
Self {
|
|
content: content.into(),
|
|
style: Style::default(),
|
|
}
|
|
}
|
|
|
|
/// Set the content of the [`Cell`]
|
|
///
|
|
/// The `content` parameter accepts any value that can be converted into a [`Text`].
|
|
///
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// # use ratatui::{prelude::*, widgets::*};
|
|
/// Cell::default().content("simple string");
|
|
/// Cell::default().content(Span::from("span"));
|
|
/// Cell::default().content(Line::from(vec![
|
|
/// Span::raw("a vec of "),
|
|
/// Span::styled("spans", Style::new().bold()),
|
|
/// ]));
|
|
/// Cell::default().content(Text::from("a text"));
|
|
/// ```
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
pub fn content<T>(mut self, content: T) -> Self
|
|
where
|
|
T: Into<Text<'a>>,
|
|
{
|
|
self.content = content.into();
|
|
self
|
|
}
|
|
|
|
/// Set the `Style` of this cell
|
|
///
|
|
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
|
|
/// your own type that implements [`Into<Style>`]).
|
|
///
|
|
/// This `Style` will override the `Style` of the [`Row`] and can be overridden by the `Style`
|
|
/// of the [`Text`] content.
|
|
///
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// # use ratatui::{prelude::*, widgets::*};
|
|
/// Cell::new("Cell 1").style(Style::new().red().italic());
|
|
/// ```
|
|
///
|
|
/// `Cell` also implements the [`Styled`] trait, which means you can use style shorthands from
|
|
/// the [`Stylize`] trait to set the style of the widget more concisely.
|
|
///
|
|
/// ```rust
|
|
/// # use ratatui::{prelude::*, widgets::*};
|
|
/// Cell::new("Cell 1").red().italic();
|
|
/// ```
|
|
///
|
|
/// [`Row`]: super::Row
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
|
|
self.style = style.into();
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Cell<'_> {
|
|
pub(crate) fn render(&self, area: Rect, buf: &mut Buffer) {
|
|
buf.set_style(area, self.style);
|
|
self.content.clone().render(area, buf);
|
|
}
|
|
}
|
|
|
|
impl<'a, T> From<T> for Cell<'a>
|
|
where
|
|
T: Into<Text<'a>>,
|
|
{
|
|
fn from(content: T) -> Cell<'a> {
|
|
Cell {
|
|
content: content.into(),
|
|
style: Style::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Styled for Cell<'a> {
|
|
type Item = Cell<'a>;
|
|
|
|
fn style(&self) -> Style {
|
|
self.style
|
|
}
|
|
|
|
fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
|
|
self.style(style)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::style::{Color, Modifier, Style, Stylize};
|
|
|
|
#[test]
|
|
fn new() {
|
|
let cell = Cell::new("");
|
|
assert_eq!(cell.content, Text::from(""));
|
|
}
|
|
|
|
#[test]
|
|
fn content() {
|
|
let cell = Cell::default().content("");
|
|
assert_eq!(cell.content, Text::from(""));
|
|
}
|
|
|
|
#[test]
|
|
fn style() {
|
|
let style = Style::default().red().italic();
|
|
let cell = Cell::default().style(style);
|
|
assert_eq!(cell.style, style);
|
|
}
|
|
|
|
#[test]
|
|
fn stylize() {
|
|
assert_eq!(
|
|
Cell::from("").black().on_white().bold().not_dim().style,
|
|
Style::default()
|
|
.fg(Color::Black)
|
|
.bg(Color::White)
|
|
.add_modifier(Modifier::BOLD)
|
|
.remove_modifier(Modifier::DIM)
|
|
)
|
|
}
|
|
}
|