veloren_voxygen/ui/ice/widget/
background_container.rs1use iced::{
2 Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size, Widget, layout,
3};
4use std::hash::Hash;
5
6#[derive(Copy, Clone, Hash)]
16pub struct Padding {
17 pub top: u16,
18 pub bottom: u16,
19 pub right: u16,
20 pub left: u16,
21}
22
23impl Padding {
24 pub fn new() -> Self {
25 Padding {
26 top: 0,
27 bottom: 0,
28 right: 0,
29 left: 0,
30 }
31 }
32
33 #[must_use]
34 pub fn top(mut self, pad: u16) -> Self {
35 self.top = pad;
36 self
37 }
38
39 #[must_use]
40 pub fn bottom(mut self, pad: u16) -> Self {
41 self.bottom = pad;
42 self
43 }
44
45 #[must_use]
46 pub fn right(mut self, pad: u16) -> Self {
47 self.right = pad;
48 self
49 }
50
51 #[must_use]
52 pub fn left(mut self, pad: u16) -> Self {
53 self.left = pad;
54 self
55 }
56
57 #[must_use]
58 pub fn vertical(mut self, pad: u16) -> Self {
59 self.top = pad;
60 self.bottom = pad;
61 self
62 }
63
64 #[must_use]
65 pub fn horizontal(mut self, pad: u16) -> Self {
66 self.left = pad;
67 self.right = pad;
68 self
69 }
70}
71
72impl Default for Padding {
73 fn default() -> Self { Self::new() }
74}
75
76pub trait Background<R: iced::Renderer>: Sized {
77 fn width(&self) -> Length;
80 fn height(&self) -> Length;
81 fn aspect_ratio_fixed(&self) -> bool;
82 fn pixel_dims(&self, renderer: &R) -> (u16, u16);
83 fn draw(
84 &self,
85 renderer: &mut R,
86 defaults: &R::Defaults,
87 layout: Layout<'_>,
88 cursor_position: Point,
89 viewport: &Rectangle,
90 ) -> R::Output;
91}
92
93pub struct BackgroundContainer<'a, M, R: Renderer, B: Background<R>> {
95 max_width: u32,
96 max_height: u32,
97 background: B,
98 padding: Padding,
101 content: Element<'a, M, R>,
102}
103
104impl<'a, M, R, B> BackgroundContainer<'a, M, R, B>
105where
106 R: Renderer,
107 B: Background<R>,
108{
109 pub fn new(background: B, content: impl Into<Element<'a, M, R>>) -> Self {
110 Self {
111 max_width: u32::MAX,
112 max_height: u32::MAX,
113 background,
114 padding: Padding::new(),
115 content: content.into(),
116 }
117 }
118
119 #[must_use]
120 pub fn padding(mut self, padding: Padding) -> Self {
121 self.padding = padding;
122 self
123 }
124
125 #[must_use]
126 pub fn max_width(mut self, max_width: u32) -> Self {
127 self.max_width = max_width;
128 self
129 }
130
131 #[must_use]
132 pub fn max_height(mut self, max_height: u32) -> Self {
133 self.max_height = max_height;
134 self
135 }
136}
137
138impl<M, R, B> Widget<M, R> for BackgroundContainer<'_, M, R, B>
139where
140 R: Renderer,
141 B: Background<R>,
142{
143 fn width(&self) -> Length { self.background.width() }
145
146 fn height(&self) -> Length { self.background.height() }
147
148 fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node {
149 let limits = limits
150 .loose() .max_width(self.max_width)
152 .max_height(self.max_height)
153 .width(self.width())
154 .height(self.height());
155
156 let (pixel_w, pixel_h) = self.background.pixel_dims(renderer);
157 let (horizontal_pad_frac, vertical_pad_frac, top_pad_frac, left_pad_frac) = {
158 let Padding {
159 top,
160 bottom,
161 right,
162 left,
163 } = self.padding;
164 debug_assert!(pixel_w != 0);
167 debug_assert!(pixel_h != 0);
168 debug_assert!(top + bottom < pixel_h);
169 debug_assert!(right + left < pixel_w);
170 (
171 (right + left) as f32 / pixel_w as f32,
172 (top + bottom) as f32 / pixel_h as f32,
173 top as f32 / pixel_h as f32,
174 left as f32 / pixel_w as f32,
175 )
176 };
177
178 let (size, content) = if self.background.aspect_ratio_fixed() {
179 let aspect_ratio = pixel_w as f32 / pixel_h as f32;
182
183 let max_size = limits.max();
186 let (max_width, max_height) = (max_size.width, max_size.height);
187 let max_aspect_ratio = max_width / max_height;
188 let limits = if max_aspect_ratio > aspect_ratio {
189 limits.max_width((max_height * aspect_ratio) as u32)
190 } else {
191 limits.max_height((max_width / aspect_ratio) as u32)
192 };
193 let limits = limits.shrink({
195 let max = limits.max();
196 Size::new(
197 max.width * horizontal_pad_frac,
198 max.height * vertical_pad_frac,
199 )
200 });
201
202 let mut content = self.content.layout(renderer, &limits.loose());
205
206 let mut content_size = content.size();
212 content_size.width /= 1.0 - horizontal_pad_frac;
215 content_size.height /= 1.0 - vertical_pad_frac;
216 let content_aspect_ratio = content_size.width / content_size.height;
217 let size = if content_aspect_ratio > aspect_ratio {
218 Size::new(content_size.width, content_size.width / aspect_ratio)
219 } else {
220 Size::new(content_size.height * aspect_ratio, content_size.height)
221 };
222
223 content.move_to(Point::new(
225 left_pad_frac * size.width,
226 top_pad_frac * size.height,
227 ));
228
229 (size, content)
230 } else {
231 let limits = limits
233 .shrink({
234 let max = limits.max();
235 Size::new(
236 max.width * horizontal_pad_frac,
237 max.height * vertical_pad_frac,
238 )
239 })
240 .loose(); let mut content = self.content.layout(renderer, &limits);
243
244 let mut size = limits.resolve(content.size());
245 size.width /= 1.0 - horizontal_pad_frac;
247 size.height /= 1.0 - vertical_pad_frac;
248
249 content.move_to(Point::new(
251 left_pad_frac * size.width,
252 top_pad_frac * size.height,
253 ));
254 (size, content)
257 };
258
259 layout::Node::with_children(size, vec![content])
260 }
261
262 fn draw(
263 &self,
264 renderer: &mut R,
265 defaults: &R::Defaults,
266 layout: Layout<'_>,
267 cursor_position: Point,
268 viewport: &Rectangle,
269 ) -> R::Output {
270 renderer.draw(
271 defaults,
272 &self.background,
273 layout,
274 viewport,
275 &self.content,
276 layout.children().next().unwrap(),
277 cursor_position,
278 )
279 }
280
281 fn hash_layout(&self, state: &mut Hasher) {
282 struct Marker;
283 std::any::TypeId::of::<Marker>().hash(state);
284
285 self.width().hash(state);
286 self.height().hash(state);
287 self.max_width.hash(state);
288 self.max_height.hash(state);
289 self.background.aspect_ratio_fixed().hash(state);
290 self.padding.hash(state);
291 self.content.hash_layout(state);
294 }
295
296 fn on_event(
297 &mut self,
298 event: Event,
299 layout: Layout<'_>,
300 cursor_position: Point,
301 renderer: &R,
302 clipboard: &mut dyn Clipboard,
303 messages: &mut Vec<M>,
304 ) -> iced::event::Status {
305 self.content.on_event(
306 event,
307 layout.children().next().unwrap(),
308 cursor_position,
309 renderer,
310 clipboard,
311 messages,
312 )
313 }
314
315 fn overlay(&mut self, layout: Layout<'_>) -> Option<iced::overlay::Element<'_, M, R>> {
316 self.content.overlay(layout.children().next().unwrap())
317 }
318}
319
320pub trait Renderer: iced::Renderer {
321 fn draw<M, B>(
322 &mut self,
323 defaults: &Self::Defaults,
324 background: &B,
325 background_layout: Layout<'_>,
326 viewport: &Rectangle,
327 content: &Element<'_, M, Self>,
328 content_layout: Layout<'_>,
329 cursor_position: Point,
330 ) -> Self::Output
331 where
332 B: Background<Self>;
333}
334
335impl<'a, M: 'a, R: 'a, B> From<BackgroundContainer<'a, M, R, B>> for Element<'a, M, R>
337where
338 R: Renderer,
339 B: 'a + Background<R>,
340{
341 fn from(background_container: BackgroundContainer<'a, M, R, B>) -> Element<'a, M, R> {
342 Element::new(background_container)
343 }
344}