ori_core/views/
pad.rs

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::ops::{Deref, DerefMut};

use ori_macro::example;

use crate::{
    context::{BuildCx, DrawCx, EventCx, LayoutCx, RebuildCx},
    event::Event,
    layout::{Padding, Size, Space},
    rebuild::Rebuild,
    view::{Pod, PodState, View},
};

/// Create a new [`Pad`] view.
pub fn pad<V>(padding: impl Into<Padding>, view: V) -> Pad<V> {
    Pad::new(padding.into(), view)
}

/// Create a new [`Pad`] view adding padding to the top.
pub fn pad_top<V>(padding: f32, view: V) -> Pad<V> {
    Pad::new([padding, 0.0, 0.0, 0.0], view)
}

/// Create a new [`Pad`] view adding padding to the right.
pub fn pad_right<V>(padding: f32, view: V) -> Pad<V> {
    Pad::new([0.0, padding, 0.0, 0.0], view)
}

/// Create a new [`Pad`] view adding padding to the bottom.
pub fn pad_bottom<V>(padding: f32, view: V) -> Pad<V> {
    Pad::new([0.0, 0.0, padding, 0.0], view)
}

/// Create a new [`Pad`] view adding padding to the left.
pub fn pad_left<V>(padding: f32, view: V) -> Pad<V> {
    Pad::new([0.0, 0.0, 0.0, padding], view)
}

/// A view that adds padding to its content.
#[example(name = "pad", width = 400, height = 300)]
#[derive(Rebuild)]
pub struct Pad<V> {
    /// The content.
    pub content: Pod<V>,

    /// The padding.
    #[rebuild(layout)]
    pub padding: Padding,
}

impl<V> Pad<V> {
    /// Create a new [`Pad`] view.
    pub fn new(padding: impl Into<Padding>, content: V) -> Self {
        Self {
            content: Pod::new(content),
            padding: padding.into(),
        }
    }
}

impl<V> Deref for Pad<V> {
    type Target = V;

    fn deref(&self) -> &Self::Target {
        &self.content
    }
}

impl<V> DerefMut for Pad<V> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.content
    }
}

impl<T, V: View<T>> View<T> for Pad<V> {
    type State = PodState<T, V>;

    fn build(&mut self, cx: &mut BuildCx, data: &mut T) -> Self::State {
        self.content.build(cx, data)
    }

    fn rebuild(&mut self, state: &mut Self::State, cx: &mut RebuildCx, data: &mut T, old: &Self) {
        Rebuild::rebuild(self, cx, old);
        self.content.rebuild(state, cx, data, &old.content);
    }

    fn event(
        &mut self,
        state: &mut Self::State,
        cx: &mut EventCx,
        data: &mut T,
        event: &Event,
    ) -> bool {
        self.content.event(state, cx, data, event)
    }

    fn layout(
        &mut self,
        state: &mut Self::State,
        cx: &mut LayoutCx,
        data: &mut T,
        space: Space,
    ) -> Size {
        let content_space = space.shrink(self.padding.size());
        let content_size = self.content.layout(state, cx, data, content_space);

        state.translate(self.padding.offset());

        space.fit(content_size + self.padding.size())
    }

    fn draw(&mut self, state: &mut Self::State, cx: &mut DrawCx, data: &mut T) {
        self.content.draw(state, cx, data);
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        layout::{Rect, Space},
        views::{
            pad, size,
            testing::{save_layout, test_layout},
        },
    };

    #[test]
    fn layout() {
        let inner = save_layout("inner", size(9.0, ()));
        let mut view = save_layout("pad", pad([3.0, 4.0, 5.0, 6.0], inner));

        let layouts = test_layout(&mut view, &mut (), Space::UNBOUNDED);

        assert_eq!(layouts["pad"], Rect::from([0.0, 0.0, 19.0, 17.0]));
        assert_eq!(layouts["inner"], Rect::from([6.0, 3.0, 15.0, 12.0]));
    }
}