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}