ratatui_widgets/table/
row.rs

1use alloc::vec::Vec;
2
3use ratatui_core::style::{Style, Styled};
4
5use super::Cell;
6
7/// A single row of data to be displayed in a [`Table`] widget.
8///
9/// A `Row` is a collection of [`Cell`]s.
10///
11/// By default, a row has a height of 1 but you can change this using [`Row::height`].
12///
13/// You can set the style of the entire row using [`Row::style`]. This [`Style`] will be combined
14/// with the [`Style`] of each individual [`Cell`] by adding the [`Style`] of the [`Cell`] to the
15/// [`Style`] of the [`Row`].
16///
17/// # Examples
18///
19/// You can create `Row`s from simple strings.
20///
21/// ```rust
22/// use ratatui::widgets::Row;
23///
24/// Row::new(vec!["Cell1", "Cell2", "Cell3"]);
25/// ```
26///
27/// If you need a bit more control over individual cells, you can explicitly create [`Cell`]s:
28///
29/// ```rust
30/// use ratatui::style::Stylize;
31/// use ratatui::widgets::{Cell, Row};
32///
33/// Row::new(vec![
34///     Cell::from("Cell1"),
35///     Cell::from("Cell2").red().italic(),
36/// ]);
37/// ```
38///
39/// You can also construct a row from any type that can be converted into [`Text`]:
40///
41/// ```rust
42/// use std::borrow::Cow;
43///
44/// use ratatui::widgets::{Cell, Row};
45///
46/// Row::new(vec![
47///     Cow::Borrowed("hello"),
48///     Cow::Owned("world".to_uppercase()),
49/// ]);
50/// ```
51///
52/// An iterator whose item type is convertible into [`Text`] can be collected into a row.
53///
54/// ```rust
55/// use ratatui::widgets::Row;
56///
57/// (0..10).map(|i| format!("{i}")).collect::<Row>();
58/// ```
59///
60/// `Row` implements [`Styled`] which means you can use style shorthands from the [`Stylize`] trait
61/// to set the style of the row concisely.
62///
63/// ```rust
64/// use ratatui::style::Stylize;
65/// use ratatui::widgets::Row;
66///
67/// let cells = vec!["Cell1", "Cell2", "Cell3"];
68/// Row::new(cells).red().italic();
69/// ```
70///
71/// [`Table`]: super::Table
72/// [`Text`]: ratatui_core::text::Text
73/// [`Stylize`]: ratatui_core::style::Stylize
74#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
75pub struct Row<'a> {
76    pub(crate) cells: Vec<Cell<'a>>,
77    pub(crate) height: u16,
78    pub(crate) top_margin: u16,
79    pub(crate) bottom_margin: u16,
80    pub(crate) style: Style,
81}
82
83impl<'a> Row<'a> {
84    /// Creates a new [`Row`]
85    ///
86    /// The `cells` parameter accepts any value that can be converted into an iterator of anything
87    /// that can be converted into a [`Cell`] (e.g. `Vec<&str>`, `&[Cell<'a>]`, `Vec<String>`, etc.)
88    ///
89    /// # Examples
90    ///
91    /// ```rust
92    /// use ratatui::widgets::{Cell, Row};
93    ///
94    /// let row = Row::new(vec!["Cell 1", "Cell 2", "Cell 3"]);
95    /// let row = Row::new(vec![
96    ///     Cell::new("Cell 1"),
97    ///     Cell::new("Cell 2"),
98    ///     Cell::new("Cell 3"),
99    /// ]);
100    /// ```
101    pub fn new<T>(cells: T) -> Self
102    where
103        T: IntoIterator,
104        T::Item: Into<Cell<'a>>,
105    {
106        Self {
107            cells: cells.into_iter().map(Into::into).collect(),
108            height: 1,
109            ..Default::default()
110        }
111    }
112
113    /// Set the cells of the [`Row`]
114    ///
115    /// The `cells` parameter accepts any value that can be converted into an iterator of anything
116    /// that can be converted into a [`Cell`] (e.g. `Vec<&str>`, `&[Cell<'a>]`, `Vec<String>`, etc.)
117    ///
118    /// This is a fluent setter method which must be chained or used as it consumes self
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// use ratatui::widgets::{Cell, Row};
124    ///
125    /// let row = Row::default().cells(vec!["Cell 1", "Cell 2", "Cell 3"]);
126    /// let row = Row::default().cells(vec![
127    ///     Cell::new("Cell 1"),
128    ///     Cell::new("Cell 2"),
129    ///     Cell::new("Cell 3"),
130    /// ]);
131    /// ```
132    #[must_use = "method moves the value of self and returns the modified value"]
133    pub fn cells<T>(mut self, cells: T) -> Self
134    where
135        T: IntoIterator,
136        T::Item: Into<Cell<'a>>,
137    {
138        self.cells = cells.into_iter().map(Into::into).collect();
139        self
140    }
141
142    /// Set the fixed height of the [`Row`]
143    ///
144    /// Any [`Cell`] whose content has more lines than this height will see its content truncated.
145    ///
146    /// By default, the height is `1`.
147    ///
148    /// This is a fluent setter method which must be chained or used as it consumes self
149    ///
150    /// # Examples
151    ///
152    /// ```rust
153    /// use ratatui::widgets::Row;
154    ///
155    /// let cells = vec!["Cell 1\nline 2", "Cell 2", "Cell 3"];
156    /// let row = Row::new(cells).height(2);
157    /// ```
158    #[must_use = "method moves the value of self and returns the modified value"]
159    pub const fn height(mut self, height: u16) -> Self {
160        self.height = height;
161        self
162    }
163
164    /// Set the top margin. By default, the top margin is `0`.
165    ///
166    /// The top margin is the number of blank lines to be displayed before the row.
167    ///
168    /// This is a fluent setter method which must be chained or used as it consumes self
169    ///
170    /// # Examples
171    ///
172    /// ```rust
173    /// use ratatui::widgets::Row;
174    /// let cells = vec!["Cell 1", "Cell 2", "Cell 3"];
175    ///
176    /// let row = Row::default().top_margin(1);
177    /// ```
178    #[must_use = "method moves the value of self and returns the modified value"]
179    pub const fn top_margin(mut self, margin: u16) -> Self {
180        self.top_margin = margin;
181        self
182    }
183
184    /// Set the bottom margin. By default, the bottom margin is `0`.
185    ///
186    /// The bottom margin is the number of blank lines to be displayed after the row.
187    ///
188    /// This is a fluent setter method which must be chained or used as it consumes self
189    ///
190    /// # Examples
191    ///
192    /// ```rust
193    /// use ratatui::widgets::Row;
194    ///
195    /// let cells = vec!["Cell 1", "Cell 2", "Cell 3"];
196    /// let row = Row::default().bottom_margin(1);
197    /// ```
198    #[must_use = "method moves the value of self and returns the modified value"]
199    pub const fn bottom_margin(mut self, margin: u16) -> Self {
200        self.bottom_margin = margin;
201        self
202    }
203
204    /// Set the [`Style`] of the entire row
205    ///
206    /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
207    /// your own type that implements [`Into<Style>`]).
208    ///
209    /// This [`Style`] can be overridden by the [`Style`] of a any individual [`Cell`] or by their
210    /// [`Text`] content.
211    ///
212    /// This is a fluent setter method which must be chained or used as it consumes self
213    ///
214    /// # Examples
215    ///
216    /// ```rust
217    /// use ratatui::style::{Style, Stylize};
218    /// use ratatui::widgets::Row;
219    /// let cells = vec!["Cell 1", "Cell 2", "Cell 3"];
220    /// let row = Row::new(cells).style(Style::new().red().italic());
221    /// ```
222    ///
223    /// `Row` also implements the [`Styled`] trait, which means you can use style shorthands from
224    /// the [`Stylize`] trait to set the style of the widget more concisely.
225    ///
226    /// ```rust
227    /// use ratatui::style::Stylize;
228    /// use ratatui::widgets::Row;
229    ///
230    /// let cells = vec!["Cell 1", "Cell 2", "Cell 3"];
231    /// let row = Row::new(cells).red().italic();
232    /// ```
233    ///
234    /// [`Color`]: ratatui_core::style::Color
235    /// [`Stylize`]: ratatui_core::style::Stylize
236    /// [`Text`]: ratatui_core::text::Text
237    #[must_use = "method moves the value of self and returns the modified value"]
238    pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
239        self.style = style.into();
240        self
241    }
242}
243
244// private methods for rendering
245impl Row<'_> {
246    /// Returns the total height of the row.
247    pub(crate) const fn height_with_margin(&self) -> u16 {
248        self.height
249            .saturating_add(self.top_margin)
250            .saturating_add(self.bottom_margin)
251    }
252}
253
254impl Styled for Row<'_> {
255    type Item = Self;
256
257    fn style(&self) -> Style {
258        self.style
259    }
260
261    fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
262        self.style(style)
263    }
264}
265
266impl<'a, Item> FromIterator<Item> for Row<'a>
267where
268    Item: Into<Cell<'a>>,
269{
270    fn from_iter<IterCells: IntoIterator<Item = Item>>(cells: IterCells) -> Self {
271        Self::new(cells)
272    }
273}
274
275#[cfg(test)]
276mod tests {
277    use alloc::vec;
278
279    use ratatui_core::style::{Color, Modifier, Stylize};
280
281    use super::*;
282
283    #[test]
284    fn new() {
285        let cells = vec![Cell::from("")];
286        let row = Row::new(cells.clone());
287        assert_eq!(row.cells, cells);
288    }
289
290    #[test]
291    fn collect() {
292        let cells = vec![Cell::from("")];
293        let row: Row = cells.iter().cloned().collect();
294        assert_eq!(row.cells, cells);
295    }
296
297    #[test]
298    fn cells() {
299        let cells = vec![Cell::from("")];
300        let row = Row::default().cells(cells.clone());
301        assert_eq!(row.cells, cells);
302    }
303
304    #[test]
305    fn height() {
306        let row = Row::default().height(2);
307        assert_eq!(row.height, 2);
308    }
309
310    #[test]
311    fn top_margin() {
312        let row = Row::default().top_margin(1);
313        assert_eq!(row.top_margin, 1);
314    }
315
316    #[test]
317    fn bottom_margin() {
318        let row = Row::default().bottom_margin(1);
319        assert_eq!(row.bottom_margin, 1);
320    }
321
322    #[test]
323    fn style() {
324        let style = Style::default().red().italic();
325        let row = Row::default().style(style);
326        assert_eq!(row.style, style);
327    }
328
329    #[test]
330    fn stylize() {
331        assert_eq!(
332            Row::new(vec![Cell::from("")])
333                .black()
334                .on_white()
335                .bold()
336                .not_italic()
337                .style,
338            Style::default()
339                .fg(Color::Black)
340                .bg(Color::White)
341                .add_modifier(Modifier::BOLD)
342                .remove_modifier(Modifier::ITALIC)
343        );
344    }
345}