veloren_voxygen/ui/ice/widget/
compound_graphic.rs1use super::image::Handle;
2use iced::{Element, Hasher, Layout, Length, Point, Rectangle, Widget, layout};
3use std::hash::Hash;
4use vek::{Aabr, Rgba, Vec2};
5
6#[derive(Copy, Clone)]
13pub enum GraphicKind {
14 Image(Handle, Rgba<u8>),
15 Color(Rgba<u8>),
16 Gradient(Rgba<u8>, Rgba<u8>),
18}
19
20pub struct Graphic {
23 aabr: Aabr<u16>,
24 kind: GraphicKind,
25}
26
27impl Graphic {
28 fn new(kind: GraphicKind, size: [u16; 2], offset: [u16; 2]) -> Self {
29 let size = Vec2::from(size);
30 let offset = Vec2::from(offset);
31 Self {
32 aabr: Aabr {
33 min: offset,
34 max: offset + size,
35 },
36 kind,
37 }
38 }
39
40 pub fn image(handle: Handle, size: [u16; 2], offset: [u16; 2]) -> Self {
41 Self::new(
42 GraphicKind::Image(handle, Rgba::broadcast(255)),
43 size,
44 offset,
45 )
46 }
47
48 #[must_use]
49 pub fn color(mut self, color: Rgba<u8>) -> Self {
50 match &mut self.kind {
51 GraphicKind::Image(_, c) => *c = color,
52 GraphicKind::Color(c) => *c = color,
53 GraphicKind::Gradient(_, _) => (),
55 }
56 self
57 }
58
59 pub fn gradient(
60 top_color: Rgba<u8>,
61 bottom_color: Rgba<u8>,
62 size: [u16; 2],
63 offset: [u16; 2],
64 ) -> Self {
65 Self::new(GraphicKind::Gradient(top_color, bottom_color), size, offset)
66 }
67
68 pub fn rect(color: Rgba<u8>, size: [u16; 2], offset: [u16; 2]) -> Self {
69 Self::new(GraphicKind::Color(color), size, offset)
70 }
71}
72
73pub struct CompoundGraphic {
74 graphics: Vec<Graphic>,
75 graphics_size: [u16; 2],
77 width: Length,
78 height: Length,
79 fix_aspect_ratio: bool,
80 }
83
84impl CompoundGraphic {
85 pub fn from_graphics(graphics: Vec<Graphic>) -> Self {
86 let width = Length::Fill;
87 let height = Length::Fill;
88 let graphics_size = graphics
89 .iter()
90 .fold(Vec2::zero(), |size, graphic| {
91 Vec2::max(size, graphic.aabr.max)
92 })
93 .into_array();
94 Self {
95 graphics,
96 graphics_size,
97 width,
98 height,
99 fix_aspect_ratio: false,
100 }
102 }
103
104 pub fn padded_image(image: Handle, size: [u16; 2], pad: [u16; 4]) -> Self {
105 let image = Graphic::image(image, size, [pad[0], pad[1]]);
106 let mut this = Self::from_graphics(vec![image]);
107 this.graphics_size[0] += pad[2];
108 this.graphics_size[1] += pad[3];
109 this
110 }
111
112 #[must_use]
113 pub fn width(mut self, width: Length) -> Self {
114 self.width = width;
115 self
116 }
117
118 #[must_use]
119 pub fn height(mut self, height: Length) -> Self {
120 self.height = height;
121 self
122 }
123
124 #[must_use]
125 pub fn fix_aspect_ratio(mut self) -> Self {
126 self.fix_aspect_ratio = true;
127 self
128 }
129
130 fn draw<R: Renderer>(&self, renderer: &mut R, layout: Layout<'_>) -> R::Output {
136 let [pixel_w, pixel_h] = self.graphics_size;
137 let bounds = layout.bounds();
138 let scale = Vec2::new(
139 bounds.width / pixel_w as f32,
140 bounds.height / pixel_h as f32,
141 );
142 let graphics = self.graphics.iter().map(|graphic| {
143 let bounds = {
144 let Aabr { min, max } = graphic.aabr.map(|e| e as f32);
145 let min = min * scale;
146 let size = max * scale - min;
147 Rectangle {
148 x: min.x + bounds.x,
149 y: min.y + bounds.y,
150 width: size.x,
151 height: size.y,
152 }
153 };
154 (bounds, graphic.kind)
155 });
156
157 renderer.draw(graphics)
158 }
159}
160
161impl<M, R> Widget<M, R> for CompoundGraphic
162where
163 R: Renderer,
164{
165 fn width(&self) -> Length { self.width }
166
167 fn height(&self) -> Length { self.height }
168
169 fn layout(&self, _renderer: &R, limits: &layout::Limits) -> layout::Node {
170 let mut size = limits.width(self.width).height(self.height).max();
171
172 if self.fix_aspect_ratio {
173 let aspect_ratio = {
174 let [w, h] = self.graphics_size;
175 w as f32 / h as f32
176 };
177
178 let max_aspect_ratio = size.width / size.height;
179
180 if max_aspect_ratio > aspect_ratio {
181 size.width = size.height * aspect_ratio;
182 } else {
183 size.height = size.width / aspect_ratio;
184 }
185 }
186
187 layout::Node::new(size)
188 }
189
190 fn draw(
191 &self,
192 renderer: &mut R,
193 _defaults: &R::Defaults,
194 layout: Layout<'_>,
195 _cursor_position: Point,
196 _viewport: &Rectangle,
198 ) -> R::Output {
199 Self::draw(self, renderer, layout)
200 }
201
202 fn hash_layout(&self, state: &mut Hasher) {
203 struct Marker;
204 std::any::TypeId::of::<Marker>().hash(state);
205
206 self.width.hash(state);
207 self.height.hash(state);
208 if self.fix_aspect_ratio {
209 self.graphics_size.hash(state);
210 }
211 }
212}
213
214pub trait Renderer: iced::Renderer {
215 fn draw<I>(&mut self, graphics: I) -> Self::Output
216 where
217 I: Iterator<Item = (Rectangle, GraphicKind)>;
218}
219
220impl<'a, M, R> From<CompoundGraphic> for Element<'a, M, R>
221where
222 R: Renderer,
223{
224 fn from(compound_graphic: CompoundGraphic) -> Element<'a, M, R> {
225 Element::new(compound_graphic)
226 }
227}
228
229impl<R> super::background_container::Background<R> for CompoundGraphic
230where
231 R: Renderer,
232{
233 fn width(&self) -> Length { self.width }
234
235 fn height(&self) -> Length { self.height }
236
237 fn aspect_ratio_fixed(&self) -> bool { self.fix_aspect_ratio }
238
239 fn pixel_dims(&self, _renderer: &R) -> (u16, u16) {
240 (self.graphics_size[0], self.graphics_size[1])
241 }
242
243 fn draw(
244 &self,
245 renderer: &mut R,
246 _defaults: &R::Defaults,
247 layout: Layout<'_>,
248 _cursor_position: Point,
249 _viewport: &Rectangle,
250 ) -> R::Output {
251 Self::draw(self, renderer, layout)
252 }
253}