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#[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
22pub 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 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 pub fn offset(mut self, offset: (f32, f32)) -> Self {
41 self.offset = offset;
42 self
43 }
44
45 pub fn anchor(mut self, anchor: Anchor) -> Self {
47 self.anchor = anchor;
48 self
49 }
50
51 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}