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