veloren_voxygen/ui/ice/widget/
image.rs

1use super::super::graphic;
2use iced::{Element, Hasher, Layout, Length, Point, Rectangle, Widget, layout};
3use std::hash::Hash;
4use vek::Rgba;
5
6// TODO: consider iced's approach to images and caching image data
7// Also `Graphic` might be a better name for this is it wasn't already in use
8// elsewhere
9
10pub type Handle = graphic::Id;
11
12pub struct Image {
13    handle: Handle,
14    width: Length,
15    height: Length,
16    fix_aspect_ratio: bool,
17    color: Rgba<u8>,
18}
19
20impl Image {
21    pub fn new(handle: Handle) -> Self {
22        let width = Length::Fill;
23        let height = Length::Fill;
24        Self {
25            handle,
26            width,
27            height,
28            fix_aspect_ratio: false,
29            color: Rgba::broadcast(255),
30        }
31    }
32
33    #[must_use]
34    pub fn width(mut self, width: Length) -> Self {
35        self.width = width;
36        self
37    }
38
39    #[must_use]
40    pub fn height(mut self, height: Length) -> Self {
41        self.height = height;
42        self
43    }
44
45    #[must_use]
46    pub fn fix_aspect_ratio(mut self) -> Self {
47        self.fix_aspect_ratio = true;
48        self
49    }
50
51    #[must_use]
52    pub fn color(mut self, color: Rgba<u8>) -> Self {
53        self.color = color;
54        self
55    }
56}
57
58impl<M, R> Widget<M, R> for Image
59where
60    R: Renderer,
61{
62    fn width(&self) -> Length { self.width }
63
64    fn height(&self) -> Length { self.height }
65
66    fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node {
67        let mut size = limits.width(self.width).height(self.height).max();
68
69        if self.fix_aspect_ratio {
70            let aspect_ratio = {
71                let (w, h) = renderer.dimensions(self.handle);
72                w as f32 / h as f32
73            };
74
75            let max_aspect_ratio = size.width / size.height;
76
77            if max_aspect_ratio > aspect_ratio {
78                size.width = size.height * aspect_ratio;
79            } else {
80                size.height = size.width / aspect_ratio;
81            }
82        }
83
84        layout::Node::new(size)
85    }
86
87    fn draw(
88        &self,
89        renderer: &mut R,
90        _defaults: &R::Defaults,
91        layout: Layout<'_>,
92        _cursor_position: Point,
93        _viewport: &Rectangle,
94    ) -> R::Output {
95        renderer.draw(self.handle, self.color, layout)
96    }
97
98    fn hash_layout(&self, state: &mut Hasher) {
99        struct Marker;
100        std::any::TypeId::of::<Marker>().hash(state);
101
102        self.width.hash(state);
103        self.height.hash(state);
104        self.fix_aspect_ratio.hash(state);
105        // TODO: also depends on dims but we have no way to access
106    }
107}
108
109pub trait Renderer: iced::Renderer {
110    fn dimensions(&self, handle: Handle) -> (u32, u32);
111    fn draw(&mut self, handle: Handle, color: Rgba<u8>, layout: Layout<'_>) -> Self::Output;
112}
113
114impl<'a, M, R> From<Image> for Element<'a, M, R>
115where
116    R: Renderer,
117{
118    fn from(image: Image) -> Element<'a, M, R> { Element::new(image) }
119}
120
121impl<R> super::background_container::Background<R> for Image
122where
123    R: Renderer,
124{
125    fn width(&self) -> Length { self.width }
126
127    fn height(&self) -> Length { self.height }
128
129    fn aspect_ratio_fixed(&self) -> bool { self.fix_aspect_ratio }
130
131    fn pixel_dims(&self, renderer: &R) -> (u16, u16) {
132        let (w, h) = renderer.dimensions(self.handle);
133        (w as u16, h as u16)
134    }
135
136    fn draw(
137        &self,
138        renderer: &mut R,
139        _defaults: &R::Defaults,
140        layout: Layout<'_>,
141        _cursor_position: Point,
142        _viewport: &Rectangle,
143    ) -> R::Output {
144        renderer.draw(self.handle, self.color, layout)
145    }
146}