1use 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#[derive(Debug, Default, Clone)]
16pub(crate) struct TextTableData<'a> {
17 pub(crate) rows: Vec<Row<'a>>,
18}
19
20#[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#[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 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 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 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 #[inline]
140 pub fn height(mut self, height: u16) -> Self {
141 self.height = height;
142 self
143 }
144
145 pub fn top_margin(mut self, margin: u16) -> Self {
147 self.top_margin = margin;
148 self
149 }
150
151 pub fn bottom_margin(mut self, margin: u16) -> Self {
153 self.bottom_margin = margin;
154 self
155 }
156
157 pub fn style(mut self, style: Option<Style>) -> Self {
159 self.style = style;
160 self
161 }
162
163 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 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 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 pub fn style(mut self, style: Option<Style>) -> Self {
233 self.style = style;
234 self
235 }
236}