pixel_widgets/widget/
panel.rs

1use crate::draw::Primitive;
2use crate::event::Event;
3use crate::layout::{Rectangle, Size};
4use crate::node::{GenericNode, IntoNode, Node};
5use crate::style::Stylesheet;
6use crate::widget::{Context, Widget};
7
8/// The anchor from which to apply the offset of a `Panel`
9#[allow(missing_docs)]
10pub enum Anchor {
11    TopLeft,
12    TopCenter,
13    TopRight,
14    CenterLeft,
15    Center,
16    CenterRight,
17    BottomLeft,
18    BottomCenter,
19    BottomRight,
20}
21
22/// A panel with a fixed size and location within it's parent
23pub struct Panel<'a, T> {
24    offset: (f32, f32),
25    anchor: Anchor,
26    content: Option<Node<'a, T>>,
27}
28
29impl<'a, T: 'a> Panel<'a, T> {
30    /// Construct a new `Panel`, with an offset from an anchor
31    pub fn new(offset: (f32, f32), anchor: Anchor, content: impl IntoNode<'a, T>) -> Self {
32        Self {
33            offset,
34            anchor,
35            content: Some(content.into_node()),
36        }
37    }
38
39    /// Sets the (x, y) offset from the anchor.
40    pub fn offset(mut self, offset: (f32, f32)) -> Self {
41        self.offset = offset;
42        self
43    }
44
45    /// Sets the anchor of the frame.
46    pub fn anchor(mut self, anchor: Anchor) -> Self {
47        self.anchor = anchor;
48        self
49    }
50
51    /// Sets the content widget from the first element of an iterator.
52    pub fn extend<I: IntoIterator<Item = N>, N: IntoNode<'a, T> + 'a>(mut self, iter: I) -> Self {
53        if self.content.is_none() {
54            self.content = iter.into_iter().next().map(IntoNode::into_node);
55        }
56        self
57    }
58
59    fn layout(&self, layout: Rectangle) -> Option<Rectangle> {
60        let (content_width, content_height) = self.content().size();
61        let (h, v) = match self.anchor {
62            Anchor::TopLeft => (0, 0),
63            Anchor::TopCenter => (0, 1),
64            Anchor::TopRight => (0, 2),
65            Anchor::CenterLeft => (1, 0),
66            Anchor::Center => (1, 1),
67            Anchor::CenterRight => (1, 2),
68            Anchor::BottomLeft => (2, 0),
69            Anchor::BottomCenter => (2, 1),
70            Anchor::BottomRight => (2, 2),
71        };
72
73        let h_available = match h {
74            0 => (layout.left + self.offset.0, layout.right),
75            1 if self.offset.0 > 0.0 => (layout.left + self.offset.0 * 2.0, layout.right),
76            1 => (layout.left, layout.right + self.offset.0 * 2.0),
77            _ => (layout.left, layout.right - self.offset.0),
78        };
79
80        let v_available = match v {
81            0 => (layout.top + self.offset.1, layout.bottom),
82            1 if self.offset.1 > 0.0 => (layout.top + self.offset.1 * 2.0, layout.bottom),
83            1 => (layout.top, layout.bottom + self.offset.1 * 2.0),
84            _ => (layout.top, layout.bottom - self.offset.1),
85        };
86
87        if h_available.0 < h_available.1 && v_available.0 < v_available.1 {
88            let width = match content_width {
89                Size::Exact(width) => width.min(h_available.1 - h_available.0),
90                Size::Fill(_) => h_available.1 - h_available.0,
91                Size::Shrink => 0.0,
92            };
93            let height = match content_height {
94                Size::Exact(height) => height.min(v_available.1 - v_available.0),
95                Size::Fill(_) => v_available.1 - v_available.0,
96                Size::Shrink => 0.0,
97            };
98
99            let (left, right) = match h {
100                0 => (h_available.0, h_available.0 + width),
101                1 => (
102                    (h_available.0 + h_available.1 - width) * 0.5,
103                    (h_available.0 + h_available.1 + width) * 0.5,
104                ),
105                _ => (h_available.1 - width, h_available.1),
106            };
107
108            let (top, bottom) = match v {
109                0 => (v_available.0, v_available.0 + height),
110                1 => (
111                    (v_available.0 + v_available.1 - height) * 0.5,
112                    (v_available.0 + v_available.1 + height) * 0.5,
113                ),
114                _ => (v_available.1 - height, v_available.1),
115            };
116
117            Some(Rectangle {
118                left,
119                right,
120                top,
121                bottom,
122            })
123        } else {
124            None
125        }
126    }
127
128    fn content(&self) -> &Node<'a, T> {
129        self.content.as_ref().expect("content of `Panel` must be set")
130    }
131
132    fn content_mut(&mut self) -> &mut Node<'a, T> {
133        self.content.as_mut().expect("content of `Panel` must be set")
134    }
135}
136
137impl<'a, T: 'a> Default for Panel<'a, T> {
138    fn default() -> Self {
139        Self {
140            offset: (0.0, 0.0),
141            anchor: Anchor::TopLeft,
142            content: None,
143        }
144    }
145}
146
147impl<'a, T: 'a> Widget<'a, T> for Panel<'a, T> {
148    type State = ();
149
150    fn mount(&self) {}
151
152    fn widget(&self) -> &'static str {
153        "panel"
154    }
155
156    fn len(&self) -> usize {
157        1
158    }
159
160    fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {
161        visitor(&mut **self.content_mut());
162    }
163
164    fn size(&self, _: &(), style: &Stylesheet) -> (Size, Size) {
165        (style.width, style.height)
166    }
167
168    fn hit(&self, _: &(), layout: Rectangle, clip: Rectangle, _: &Stylesheet, x: f32, y: f32) -> bool {
169        if layout.point_inside(x, y) && clip.point_inside(x, y) {
170            self.layout(layout)
171                .map(|layout| layout.point_inside(x, y))
172                .unwrap_or(false)
173        } else {
174            false
175        }
176    }
177
178    fn focused(&self, _: &()) -> bool {
179        self.content().focused()
180    }
181
182    fn event(
183        &mut self,
184        _: &mut (),
185        layout: Rectangle,
186        clip: Rectangle,
187        _: &Stylesheet,
188        event: Event,
189        context: &mut Context<T>,
190    ) {
191        if let Some(layout) = self.layout(layout) {
192            self.content_mut().event(layout, clip, event, context)
193        }
194    }
195
196    fn draw(&mut self, _: &mut (), layout: Rectangle, clip: Rectangle, _: &Stylesheet) -> Vec<Primitive<'a>> {
197        if let Some(layout) = self.layout(layout) {
198            self.content_mut().draw(layout, clip)
199        } else {
200            Vec::new()
201        }
202    }
203}
204
205impl<'a, T: 'a> IntoNode<'a, T> for Panel<'a, T> {
206    fn into_node(self) -> Node<'a, T> {
207        Node::from_widget(self)
208    }
209}