ratatui/widgets/table/
row.rs

1use crate::{
2    style::{Style, Styled},
3    widgets::table::Cell,
4};
5
6/// A single row of data to be displayed in a [`Table`] widget.
7///
8/// A `Row` is a collection of [`Cell`]s.
9///
10/// By default, a row has a height of 1 but you can change this using [`Row::height`].
11///
12/// You can set the style of the entire row using [`Row::style`]. This [`Style`] will be combined
13/// with the [`Style`] of each individual [`Cell`] by adding the [`Style`] of the [`Cell`] to the
14/// [`Style`] of the [`Row`].
15///
16/// # Examples
17///
18/// You can create `Row`s from simple strings.
19///
20/// ```rust
21/// use ratatui::widgets::Row;
22///
23/// Row::new(vec!["Cell1", "Cell2", "Cell3"]);
24/// ```
25///
26/// If you need a bit more control over individual cells, you can explicitly create [`Cell`]s:
27///
28/// ```rust
29/// use ratatui::{
30///     style::Stylize,
31///     widgets::{Cell, Row},
32/// };
33///
34/// Row::new(vec![
35///     Cell::from("Cell1"),
36///     Cell::from("Cell2").red().italic(),
37/// ]);
38/// ```
39///
40/// You can also construct a row from any type that can be converted into [`Text`]:
41///
42/// ```rust
43/// use std::borrow::Cow;
44///
45/// use ratatui::widgets::{Cell, Row};
46///
47/// Row::new(vec![
48///     Cow::Borrowed("hello"),
49///     Cow::Owned("world".to_uppercase()),
50/// ]);
51/// ```
52///
53/// An iterator whose item type is convertible into [`Text`] can be collected into a row.
54///
55/// ```rust
56/// use ratatui::widgets::Row;
57///
58/// (0..10).map(|i| format!("{i}")).collect::<Row>();
59/// ```
60///
61/// `Row` implements [`Styled`] which means you can use style shorthands from the [`Stylize`] trait
62/// to set the style of the row concisely.
63///
64/// ```rust
65/// use ratatui::{style::Stylize, widgets::Row};
66///
67/// let cells = vec!["Cell1", "Cell2", "Cell3"];
68/// Row::new(cells).red().italic();
69/// ```
70///
71/// [`Table`]: crate::widgets::Table
72/// [`Text`]: crate::text::Text
73/// [`Stylize`]: crate::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::{
218    ///     style::{Style, Stylize},
219    ///     widgets::Row,
220    /// };
221    /// let cells = vec!["Cell 1", "Cell 2", "Cell 3"];
222    /// let row = Row::new(cells).style(Style::new().red().italic());
223    /// ```
224    ///
225    /// `Row` also implements the [`Styled`] trait, which means you can use style shorthands from
226    /// the [`Stylize`] trait to set the style of the widget more concisely.
227    ///
228    /// ```rust
229    /// use ratatui::{style::Stylize, widgets::Row};
230    ///
231    /// let cells = vec!["Cell 1", "Cell 2", "Cell 3"];
232    /// let row = Row::new(cells).red().italic();
233    /// ```
234    ///
235    /// [`Color`]: crate::style::Color
236    /// [`Stylize`]: crate::style::Stylize
237    /// [`Text`]: crate::text::Text
238    #[must_use = "method moves the value of self and returns the modified value"]
239    pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
240        self.style = style.into();
241        self
242    }
243}
244
245// private methods for rendering
246impl Row<'_> {
247    /// Returns the total height of the row.
248    pub(crate) const fn height_with_margin(&self) -> u16 {
249        self.height
250            .saturating_add(self.top_margin)
251            .saturating_add(self.bottom_margin)
252    }
253}
254
255impl<'a> Styled for Row<'a> {
256    type Item = Self;
257
258    fn style(&self) -> Style {
259        self.style
260    }
261
262    fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
263        self.style(style)
264    }
265}
266
267impl<'a, Item> FromIterator<Item> for Row<'a>
268where
269    Item: Into<Cell<'a>>,
270{
271    fn from_iter<IterCells: IntoIterator<Item = Item>>(cells: IterCells) -> Self {
272        Self::new(cells)
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use std::vec;
279
280    use super::*;
281    use crate::style::{Color, Modifier, Stylize};
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}