rat_ftable/
textdata.rs

1//!
2//! Implements a Row and a Cell struct that are compatible to ratatui.
3//! You only need these if you use preformatted data.
4//!
5
6use crate::_private::NonExhaustive;
7use crate::{TableContext, TableData};
8use ratatui::buffer::Buffer;
9use ratatui::layout::Rect;
10use ratatui::style::{Style, Styled};
11use ratatui::text::Text;
12use ratatui::widgets::Widget;
13
14/// Internal impl for TableData using pre-rendered Cells.
15#[derive(Debug, Default, Clone)]
16pub(crate) struct TextTableData<'a> {
17    pub(crate) rows: Vec<Row<'a>>,
18}
19
20/// Rows of the table.
21#[derive(Debug, Clone)]
22pub struct Row<'a> {
23    pub cells: Vec<Cell<'a>>,
24    pub top_margin: u16,
25    pub height: u16,
26    pub bottom_margin: u16,
27    pub style: Option<Style>,
28
29    pub non_exhaustive: NonExhaustive,
30}
31
32/// A single cell of the table.
33#[derive(Debug, Clone)]
34pub struct Cell<'a> {
35    pub content: Text<'a>,
36    pub style: Option<Style>,
37
38    pub non_exhaustive: NonExhaustive,
39}
40
41impl<'a> TableData<'a> for TextTableData<'a> {
42    fn rows(&self) -> usize {
43        self.rows.len()
44    }
45
46    fn row_height(&self, r: usize) -> u16 {
47        if let Some(row) = self.rows.get(r) {
48            row.top_margin + row.height + row.bottom_margin
49        } else {
50            0
51        }
52    }
53
54    fn row_style(&self, r: usize) -> Option<Style> {
55        if let Some(row) = self.rows.get(r) {
56            row.style
57        } else {
58            None
59        }
60    }
61
62    fn render_cell(&self, _ctx: &TableContext, c: usize, r: usize, area: Rect, buf: &mut Buffer) {
63        if let Some(row) = self.rows.get(r) {
64            if let Some(cell) = row.cell(c) {
65                if let Some(style) = cell.style {
66                    buf.set_style(area, style);
67                }
68                cell.content.clone().render(area, buf);
69            }
70        }
71    }
72}
73
74impl Default for Row<'_> {
75    fn default() -> Self {
76        Self {
77            cells: Default::default(),
78            top_margin: 0,
79            height: 0,
80            bottom_margin: 0,
81            style: Default::default(),
82            non_exhaustive: NonExhaustive,
83        }
84    }
85}
86
87impl Styled for Row<'_> {
88    type Item = Self;
89
90    fn style(&self) -> Style {
91        self.style.unwrap_or_default()
92    }
93
94    fn set_style<S: Into<Style>>(mut self, style: S) -> Self::Item {
95        self.style = Some(style.into());
96        self
97    }
98}
99
100impl<'a, Item> FromIterator<Item> for Row<'a>
101where
102    Item: Into<Cell<'a>>,
103{
104    fn from_iter<T: IntoIterator<Item = Item>>(cells: T) -> Self {
105        Self::new(cells)
106    }
107}
108
109impl<'a> Row<'a> {
110    /// New row of data cells.
111    pub fn new<T>(cells: T) -> Self
112    where
113        T: IntoIterator,
114        T::Item: Into<Cell<'a>>,
115    {
116        let mut s = Self {
117            cells: cells.into_iter().map(|v| v.into()).collect(),
118            height: 1,
119            ..Default::default()
120        };
121        // content heigth
122        if let Some(height) = s.cells.iter().map(|v| v.content.height()).max() {
123            s.height = height as u16;
124        }
125        s
126    }
127
128    /// Set the data cells for the row.
129    pub fn cells<T>(mut self, cells: T) -> Self
130    where
131        T: IntoIterator,
132        T::Item: Into<Cell<'a>>,
133    {
134        self.cells = cells.into_iter().map(Into::into).collect();
135        self
136    }
137
138    /// Set the row-height.
139    #[inline]
140    pub fn height(mut self, height: u16) -> Self {
141        self.height = height;
142        self
143    }
144
145    /// Add some margin.
146    pub fn top_margin(mut self, margin: u16) -> Self {
147        self.top_margin = margin;
148        self
149    }
150
151    /// Add some margin.
152    pub fn bottom_margin(mut self, margin: u16) -> Self {
153        self.bottom_margin = margin;
154        self
155    }
156
157    /// Rowstyle.
158    pub fn style(mut self, style: Option<Style>) -> Self {
159        self.style = style;
160        self
161    }
162
163    /// Access to the cell.
164    pub fn cell<'b: 'a>(&'b self, c: usize) -> Option<&'a Cell<'a>> {
165        if let Some(t) = self.cells.get(c) {
166            Some(t)
167        } else {
168            None
169        }
170    }
171}
172
173impl Default for Cell<'_> {
174    fn default() -> Self {
175        Self {
176            content: Default::default(),
177            style: Default::default(),
178            non_exhaustive: NonExhaustive,
179        }
180    }
181}
182
183impl<'a, T> From<T> for Cell<'a>
184where
185    T: Into<Text<'a>>,
186{
187    fn from(value: T) -> Self {
188        Self {
189            content: value.into(),
190            style: Default::default(),
191            non_exhaustive: NonExhaustive,
192        }
193    }
194}
195
196impl Styled for Cell<'_> {
197    type Item = Self;
198
199    fn style(&self) -> Style {
200        self.style.unwrap_or_default()
201    }
202
203    fn set_style<S: Into<Style>>(mut self, style: S) -> Self {
204        self.style = Some(style.into());
205        self
206    }
207}
208
209impl<'a> Cell<'a> {
210    /// New Cell.
211    pub fn new<T>(content: T) -> Self
212    where
213        T: Into<Text<'a>>,
214    {
215        Self {
216            content: content.into(),
217            style: Default::default(),
218            non_exhaustive: NonExhaustive,
219        }
220    }
221
222    /// Set the cell content.
223    pub fn content<T>(mut self, content: T) -> Self
224    where
225        T: Into<Text<'a>>,
226    {
227        self.content = content.into();
228        self
229    }
230
231    /// Cell style.
232    pub fn style(mut self, style: Option<Style>) -> Self {
233        self.style = style;
234        self
235    }
236}