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}