summaryrefslogtreecommitdiff
path: root/src/library/elements/table.rs
blob: 555dcc449e52bb807319aad280b4d4f4b3f4491c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use crate::library::layout::{GridNode, TrackSizing};
use crate::library::prelude::*;

/// A table of items.
#[derive(Debug, Hash)]
pub struct TableNode {
    /// Defines sizing for content rows and columns.
    pub tracks: Spec<Vec<TrackSizing>>,
    /// Defines sizing of gutter rows and columns between content.
    pub gutter: Spec<Vec<TrackSizing>>,
    /// The nodes to be arranged in the table.
    pub children: Vec<Template>,
}

#[class]
impl TableNode {
    /// The primary cell fill color.
    pub const PRIMARY: Option<Paint> = None;
    /// The secondary cell fill color.
    pub const SECONDARY: Option<Paint> = None;
    /// How the stroke the cells.
    pub const STROKE: Option<Paint> = Some(Color::BLACK.into());
    /// The stroke's thickness.
    pub const THICKNESS: Length = Length::pt(1.0);
    /// How much to pad the cells's content.
    pub const PADDING: Linear = Length::pt(5.0).into();

    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
        let columns = args.named("columns")?.unwrap_or_default();
        let rows = args.named("rows")?.unwrap_or_default();
        let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
        let column_gutter = args.named("column-gutter")?;
        let row_gutter = args.named("row-gutter")?;
        Ok(Template::show(Self {
            tracks: Spec::new(columns, rows),
            gutter: Spec::new(
                column_gutter.unwrap_or_else(|| base_gutter.clone()),
                row_gutter.unwrap_or(base_gutter),
            ),
            children: args.all()?,
        }))
    }

    fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
        let fill = args.named("fill")?;
        styles.set_opt(Self::PRIMARY, args.named("primary")?.or(fill));
        styles.set_opt(Self::SECONDARY, args.named("secondary")?.or(fill));
        styles.set_opt(Self::STROKE, args.named("stroke")?);
        styles.set_opt(Self::THICKNESS, args.named("thickness")?);
        styles.set_opt(Self::PADDING, args.named("padding")?);
        Ok(())
    }
}

impl Show for TableNode {
    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
        if let Some(template) = styles.show(
            self,
            ctx,
            self.children.iter().map(|child| Value::Template(child.clone())),
        )? {
            return Ok(template);
        }

        let primary = styles.get(Self::PRIMARY);
        let secondary = styles.get(Self::SECONDARY);
        let thickness = styles.get(Self::THICKNESS);
        let stroke = styles.get(Self::STROKE).map(|paint| Stroke { paint, thickness });
        let padding = styles.get(Self::PADDING);

        let cols = self.tracks.x.len().max(1);
        let children = self
            .children
            .iter()
            .cloned()
            .enumerate()
            .map(|(i, child)| {
                let mut child = child.pack().padded(Sides::splat(padding));

                if let Some(stroke) = stroke {
                    child = child.stroked(stroke);
                }

                let x = i % cols;
                let y = i / cols;
                if let Some(fill) = [primary, secondary][(x + y) % 2] {
                    child = child.filled(fill);
                }

                child
            })
            .collect();

        Ok(Template::block(GridNode {
            tracks: self.tracks.clone(),
            gutter: self.gutter.clone(),
            children,
        }))
    }
}