1use crate::core;
3use crate::core::alignment;
4use crate::core::layout;
5use crate::core::mouse;
6use crate::core::overlay;
7use crate::core::renderer;
8use crate::core::widget;
9use crate::core::{
10 Alignment, Background, Element, Layout, Length, Pixels, Rectangle, Size, Widget,
11};
12
13pub fn table<'a, 'b, T, Message, Theme, Renderer>(
18 columns: impl IntoIterator<Item = Column<'a, 'b, T, Message, Theme, Renderer>>,
19 rows: impl IntoIterator<Item = T>,
20) -> Table<'a, Message, Theme, Renderer>
21where
22 T: Clone,
23 Theme: Catalog,
24{
25 Table::new(columns, rows)
26}
27
28pub fn column<'a, 'b, T, W, Message, Theme, Renderer>(
33 header: impl Widget<Message, Theme, Renderer> + 'a,
34 view: impl Fn(T) -> W + 'b,
35) -> Column<'a, 'b, T, Message, Theme, Renderer>
36where
37 T: 'a,
38 W: Widget<Message, Theme, Renderer> + 'a,
39{
40 Column {
41 header: header._boxed(),
42 view: Box::new(move |data| view(data)._boxed()),
43 width: Length::Fit,
44 align_x: alignment::Horizontal::Left,
45 align_y: alignment::Vertical::Top,
46 }
47}
48
49pub struct Table<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
51where
52 Theme: Catalog,
53{
54 columns: Vec<Column_>,
55 cells: Vec<Element<'a, Message, Theme, Renderer>>,
56 width: Length,
57 height: Length,
58 padding_x: f32,
59 padding_y: f32,
60 separator_x: f32,
61 separator_y: f32,
62 class: Theme::Class<'a>,
63}
64
65struct Column_ {
66 width: Length,
67 align_x: alignment::Horizontal,
68 align_y: alignment::Vertical,
69}
70
71impl<'a, Message, Theme, Renderer> Table<'a, Message, Theme, Renderer>
72where
73 Theme: Catalog,
74{
75 pub fn new<'b, T>(
80 columns: impl IntoIterator<Item = Column<'a, 'b, T, Message, Theme, Renderer>>,
81 rows: impl IntoIterator<Item = T>,
82 ) -> Self
83 where
84 T: Clone,
85 {
86 let columns = columns.into_iter();
87 let rows = rows.into_iter();
88
89 let mut width = Length::Fit;
90 let mut cells = Vec::with_capacity(columns.size_hint().0 * (1 + rows.size_hint().0));
91
92 let (columns, views): (Vec<_>, Vec<_>) = columns
93 .map(|column| {
94 width = width.stack(column.width);
95
96 cells.push(column.header);
97
98 (
99 Column_ {
100 width: column.width,
101 align_x: column.align_x,
102 align_y: column.align_y,
103 },
104 column.view,
105 )
106 })
107 .collect();
108
109 for row in rows {
110 for view in &views {
111 let cell = view(row.clone());
112 cells.push(cell);
113 }
114 }
115
116 Self {
117 columns,
118 cells,
119 width,
120 height: Length::Fit,
121 padding_x: 10.0,
122 padding_y: 5.0,
123 separator_x: 1.0,
124 separator_y: 1.0,
125 class: Theme::default(),
126 }
127 }
128
129 pub fn width(mut self, width: impl Into<Length>) -> Self {
131 self.width = width.into();
132 self
133 }
134
135 pub fn padding(self, padding: impl Into<Pixels>) -> Self {
137 let padding = padding.into();
138
139 self.padding_x(padding).padding_y(padding)
140 }
141
142 pub fn padding_x(mut self, padding: impl Into<Pixels>) -> Self {
144 self.padding_x = padding.into().0;
145 self
146 }
147
148 pub fn padding_y(mut self, padding: impl Into<Pixels>) -> Self {
150 self.padding_y = padding.into().0;
151 self
152 }
153
154 pub fn separator(self, separator: impl Into<Pixels>) -> Self {
156 let separator = separator.into();
157
158 self.separator_x(separator).separator_y(separator)
159 }
160
161 pub fn separator_x(mut self, separator: impl Into<Pixels>) -> Self {
163 self.separator_x = separator.into().0;
164 self
165 }
166
167 pub fn separator_y(mut self, separator: impl Into<Pixels>) -> Self {
169 self.separator_y = separator.into().0;
170 self
171 }
172}
173
174struct Metrics {
175 columns: Vec<f32>,
176 rows: Vec<f32>,
177}
178
179impl<'a, Message, Theme, Renderer> widget::Meta for Table<'a, Message, Theme, Renderer> where
180 Theme: Catalog
181{
182}
183
184impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
185 for Table<'a, Message, Theme, Renderer>
186where
187 Theme: Catalog,
188 Renderer: core::Renderer,
189{
190 fn size(&self) -> Size<Length> {
191 Size {
192 width: self.width,
193 height: self.height,
194 }
195 }
196
197 fn tag(&self) -> widget::tree::Tag {
198 widget::tree::Tag::of::<Metrics>()
199 }
200
201 fn state(&self) -> widget::tree::State {
202 widget::tree::State::new(Metrics {
203 columns: Vec::new(),
204 rows: Vec::new(),
205 })
206 }
207
208 fn diff(&mut self, tree: &mut widget::Tree) {
209 tree.diff_children(&mut self.cells);
210
211 for cell in &self.cells {
212 let size = cell.size();
213
214 self.height = self.height.stack(size.height);
215 }
216 }
217
218 fn layout(&mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits) {
219 let metrics = tree.state.downcast_mut::<Metrics>();
220 let columns = self.columns.len();
221 let rows = self.cells.len() / columns;
222
223 let limits = limits.width(self.width).height(self.height);
224 let available = limits.max;
225 let table_fluid = if self.width.fill_factor() == 0 {
226 Length::Fit
227 } else {
228 Length::Fill
229 };
230
231 metrics.columns = vec![0.0; self.columns.len()];
232 metrics.rows = vec![0.0; rows];
233
234 let mut column_factors = vec![0; self.columns.len()];
235 let mut total_row_factors = 0;
236 let mut total_fluid_height = 0.0;
237 let mut row_factor = 0;
238
239 let spacing_x = self.padding_x * 2.0 + self.separator_x;
240 let spacing_y = self.padding_y * 2.0 + self.separator_y;
241
242 let mut x = self.padding_x;
245 let mut y = self.padding_y;
246
247 for (i, (cell, state)) in self.cells.iter_mut().zip(&mut tree.children).enumerate() {
248 let row = i / columns;
249 let column = i % columns;
250
251 let width = self.columns[column].width;
252 let size = cell.size();
253
254 if column == 0 {
255 x = self.padding_x;
256
257 if row > 0 {
258 y += metrics.rows[row - 1] + spacing_y;
259
260 if row_factor != 0 {
261 total_fluid_height += metrics.rows[row - 1];
262 total_row_factors += row_factor;
263
264 row_factor = 0;
265 }
266 }
267 }
268
269 let width_factor = width.fill_factor();
270 let height_factor = size.height.fill_factor();
271
272 if width_factor != 0 || height_factor != 0 || size.width.is_fill() {
273 column_factors[column] = column_factors[column].max(width_factor);
274
275 row_factor = row_factor.max(height_factor);
276
277 continue;
278 }
279
280 let limits = layout::Limits::with_flags(
281 Size::ZERO,
282 Size::new(available.width - x, available.height - y),
283 limits.compression,
284 limits.infinite,
285 )
286 .width(width);
287
288 cell.layout(state, renderer, &limits);
289 let size = limits.resolve(width, Length::Fit, state.size);
290
291 metrics.columns[column] = metrics.columns[column].max(size.width);
292 metrics.rows[row] = metrics.rows[row].max(size.height);
293
294 x += size.width + spacing_x;
295 }
296
297 let left = Size::new(
300 available.width
301 - metrics
302 .columns
303 .iter()
304 .enumerate()
305 .filter(|(i, _)| column_factors[*i] == 0)
306 .map(|(_, width)| width)
307 .sum::<f32>(),
308 available.height - total_fluid_height,
309 );
310
311 let width_unit = (left.width
312 - spacing_x * self.columns.len().saturating_sub(1) as f32
313 - self.padding_x * 2.0)
314 / column_factors.iter().sum::<u16>() as f32;
315
316 let height_unit =
317 (left.height - spacing_y * rows.saturating_sub(1) as f32 - self.padding_y * 2.0)
318 / total_row_factors as f32;
319
320 let mut x = self.padding_x;
321 let mut y = self.padding_y;
322
323 for (i, (cell, state)) in self.cells.iter_mut().zip(&mut tree.children).enumerate() {
324 let row = i / columns;
325 let column = i % columns;
326
327 let size = cell.size();
328
329 let width = self.columns[column].width;
330 let width_factor = width.fill_factor();
331 let height_factor = size.height.fill_factor();
332
333 if column == 0 {
334 x = self.padding_x;
335
336 if row > 0 {
337 y += metrics.rows[row - 1] + spacing_y;
338 }
339 }
340
341 if width_factor == 0 && size.width.fill_factor() == 0 && size.height.fill_factor() == 0
342 {
343 continue;
344 }
345
346 let max_width = if width_factor == 0 {
347 if size.width.is_fill() {
348 metrics.columns[column]
349 } else {
350 (available.width - x).max(0.0)
351 }
352 } else {
353 width_unit * width_factor as f32
354 };
355
356 let max_height = if height_factor == 0 {
357 if size.height.is_fill() {
358 metrics.rows[row]
359 } else {
360 (available.height - y).max(0.0)
361 }
362 } else {
363 height_unit * height_factor as f32
364 };
365
366 let limits = layout::Limits::with_flags(
367 Size::ZERO,
368 Size::new(max_width, max_height),
369 limits.compression,
370 limits.infinite,
371 )
372 .width(width);
373
374 cell.layout(state, renderer, &limits);
375 let size = limits.resolve(
376 if let Length::Fixed(_) = width {
377 width
378 } else {
379 table_fluid
380 },
381 Length::Fit,
382 state.size,
383 );
384
385 metrics.columns[column] = metrics.columns[column].max(size.width);
386 metrics.rows[row] = metrics.rows[row].max(size.height);
387
388 x += size.width + spacing_x;
389 }
390
391 let mut x = self.padding_x;
394 let mut y = self.padding_y;
395
396 for (i, cell) in tree.children.iter_mut().enumerate() {
397 let row = i / columns;
398 let column = i % columns;
399
400 if column == 0 {
401 x = self.padding_x;
402
403 if row > 0 {
404 y += metrics.rows[row - 1] + spacing_y;
405 }
406 }
407
408 let Column_ {
409 align_x, align_y, ..
410 } = &self.columns[column];
411
412 let position = core::Vector::new(x, y);
413
414 let alignment = cell.size.align(
415 Size::new(metrics.columns[column], metrics.rows[row]),
416 Alignment::from(*align_x),
417 Alignment::from(*align_y),
418 );
419
420 cell.translation = position + alignment;
421
422 x += metrics.columns[column] + spacing_x;
423 }
424
425 let intrinsic = limits.resolve(
426 self.width,
427 self.height,
428 Size::new(
429 x - spacing_x + self.padding_x,
430 y + metrics
431 .rows
432 .last()
433 .copied()
434 .map(|height| height + self.padding_y)
435 .unwrap_or_default(),
436 ),
437 );
438
439 tree.size = intrinsic;
440 }
441
442 fn update(
443 &mut self,
444 tree: &mut widget::Tree,
445 event: &core::Event,
446 layout: Layout,
447 cursor: mouse::Cursor,
448 renderer: &Renderer,
449 shell: &mut core::Shell<'_, Message>,
450 viewport: &Rectangle,
451 ) {
452 for (cell, (layout, tree)) in self
453 .cells
454 .iter_mut()
455 .zip(layout.iter_mut(&mut tree.children))
456 {
457 cell.update(tree, event, layout, cursor, renderer, shell, viewport);
458 }
459 }
460
461 fn draw(
462 &self,
463 tree: &widget::Tree,
464 renderer: &mut Renderer,
465 theme: &Theme,
466 style: &renderer::Style,
467 layout: Layout,
468 cursor: mouse::Cursor,
469 viewport: &Rectangle,
470 ) {
471 for (cell, (layout, state)) in self.cells.iter().zip(layout.iter(&tree.children)) {
472 cell.draw(state, renderer, theme, style, layout, cursor, viewport);
473 }
474
475 let bounds = layout.bounds();
476 let metrics = tree.state.downcast_ref::<Metrics>();
477 let style = theme.style(&self.class);
478
479 if self.separator_x > 0.0 {
480 let mut x = self.padding_x;
481
482 for width in &metrics.columns[..metrics.columns.len().saturating_sub(1)] {
483 x += width + self.padding_x;
484
485 renderer.fill_quad(
486 renderer::Quad {
487 bounds: Rectangle {
488 x: bounds.x + x,
489 y: bounds.y,
490 width: self.separator_x,
491 height: bounds.height,
492 },
493 snap: true,
494 ..renderer::Quad::default()
495 },
496 style.separator_x,
497 );
498
499 x += self.separator_x + self.padding_x;
500 }
501 }
502
503 if self.separator_y > 0.0 {
504 let mut y = self.padding_y;
505
506 for height in &metrics.rows[..metrics.rows.len().saturating_sub(1)] {
507 y += height + self.padding_y;
508
509 renderer.fill_quad(
510 renderer::Quad {
511 bounds: Rectangle {
512 x: bounds.x,
513 y: bounds.y + y,
514 width: bounds.width,
515 height: self.separator_y,
516 },
517 snap: true,
518 ..renderer::Quad::default()
519 },
520 style.separator_y,
521 );
522
523 y += self.separator_y + self.padding_y;
524 }
525 }
526 }
527
528 fn mouse_interaction(
529 &self,
530 tree: &widget::Tree,
531 layout: Layout,
532 cursor: mouse::Cursor,
533 viewport: &Rectangle,
534 renderer: &Renderer,
535 ) -> mouse::Interaction {
536 self.cells
537 .iter()
538 .zip(layout.iter(&tree.children))
539 .map(|(cell, (layout, tree))| {
540 cell.mouse_interaction(tree, layout, cursor, viewport, renderer)
541 })
542 .max()
543 .unwrap_or_default()
544 }
545
546 fn operate(
547 &mut self,
548 tree: &mut widget::Tree,
549 layout: Layout,
550 viewport: &Rectangle,
551 renderer: &Renderer,
552 operation: &mut dyn widget::Operation,
553 ) {
554 for (cell, (layout, state)) in self
555 .cells
556 .iter_mut()
557 .zip(layout.iter_mut(&mut tree.children))
558 {
559 cell.operate(state, layout, viewport, renderer, operation);
560 }
561 }
562
563 fn overlay<'b>(
564 &'b mut self,
565 tree: &'b mut widget::Tree,
566 layout: Layout,
567 renderer: &Renderer,
568 viewport: &Rectangle,
569 translation: core::Vector,
570 window: Size,
571 ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
572 overlay::from_children(
573 &mut self.cells,
574 tree,
575 layout,
576 renderer,
577 viewport,
578 translation,
579 window,
580 )
581 }
582}
583
584pub struct Column<'a, 'b, T, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
586 header: Element<'a, Message, Theme, Renderer>,
587 view: Box<dyn Fn(T) -> Element<'a, Message, Theme, Renderer> + 'b>,
588 width: Length,
589 align_x: alignment::Horizontal,
590 align_y: alignment::Vertical,
591}
592
593impl<'a, 'b, T, Message, Theme, Renderer> Column<'a, 'b, T, Message, Theme, Renderer> {
594 pub fn width(mut self, width: impl Into<Length>) -> Self {
596 self.width = width.into();
597 self
598 }
599
600 pub fn align_x(mut self, alignment: impl Into<alignment::Horizontal>) -> Self {
602 self.align_x = alignment.into();
603 self
604 }
605
606 pub fn align_y(mut self, alignment: impl Into<alignment::Vertical>) -> Self {
608 self.align_y = alignment.into();
609 self
610 }
611}
612
613#[derive(Debug, Clone, Copy)]
615pub struct Style {
616 pub separator_x: Background,
618 pub separator_y: Background,
620}
621
622pub trait Catalog {
624 type Class<'a>;
626
627 fn default<'a>() -> Self::Class<'a>;
629
630 fn style(&self, class: &Self::Class<'_>) -> Style;
632}
633
634pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>;
636
637impl<Theme> From<Style> for StyleFn<'_, Theme> {
638 fn from(style: Style) -> Self {
639 Box::new(move |_theme| style)
640 }
641}
642
643impl Catalog for crate::Theme {
644 type Class<'a> = StyleFn<'a, Self>;
645
646 fn default<'a>() -> Self::Class<'a> {
647 Box::new(default)
648 }
649
650 fn style(&self, class: &Self::Class<'_>) -> Style {
651 class(self)
652 }
653}
654
655pub fn default(theme: &crate::Theme) -> Style {
657 let palette = theme.palette();
658 let separator = palette.background.strong.color.into();
659
660 Style {
661 separator_x: separator,
662 separator_y: separator,
663 }
664}