veloren_voxygen/ui/widgets/
image_frame.rs

1//! A widget for selecting a single value along some linear range.
2use conrod_core::{
3    Color, Colorable, Positionable, Rect, Sizeable, UiCell, Widget, WidgetCommon, builder_methods,
4    image,
5    widget::{self, Image, Rectangle},
6    widget_ids,
7};
8
9#[derive(Clone, WidgetCommon)]
10pub struct ImageFrame {
11    #[conrod(common_builder)]
12    common: widget::CommonBuilder,
13    // Edge images [t, b, r, l]
14    edges: [image::Id; 4],
15    edge_src_rects: [Option<Rect>; 4],
16    // Corner images [tr, tl, br, bl]
17    corners: [image::Id; 4],
18    corner_src_rects: [Option<Rect>; 4],
19    // Center
20    center: Center,
21    // Thickness of the frame border, determines the size used for the edge and corner images
22    border_size: BorderSize,
23    // Color to apply to all images making up the image frame
24    color: Option<Color>,
25    // TODO: would it be useful to have an optional close button be a part of this?
26}
27
28#[derive(Clone)]
29pub enum Center {
30    Plain(Color),
31    Image(image::Id, Option<Rect>),
32}
33impl From<Color> for Center {
34    fn from(color: Color) -> Self { Center::Plain(color) }
35}
36impl From<image::Id> for Center {
37    fn from(image: image::Id) -> Self { Center::Image(image, None) }
38}
39impl From<(image::Id, Rect)> for Center {
40    fn from((image, src_rect): (image::Id, Rect)) -> Self { Center::Image(image, Some(src_rect)) }
41}
42
43#[derive(Clone)]
44pub struct BorderSize {
45    top: f64,
46    bottom: f64,
47    right: f64,
48    left: f64,
49}
50impl From<f64> for BorderSize {
51    fn from(thickness: f64) -> Self {
52        BorderSize {
53            top: thickness,
54            bottom: thickness,
55            right: thickness,
56            left: thickness,
57        }
58    }
59}
60impl From<[f64; 2]> for BorderSize {
61    fn from([vertical, horizontal]: [f64; 2]) -> Self {
62        BorderSize {
63            top: horizontal,
64            bottom: horizontal,
65            right: vertical,
66            left: vertical,
67        }
68    }
69}
70impl From<[f64; 4]> for BorderSize {
71    fn from(vals: [f64; 4]) -> Self {
72        BorderSize {
73            top: vals[0],
74            bottom: vals[1],
75            right: vals[2],
76            left: vals[3],
77        }
78    }
79}
80
81widget_ids! {
82    struct Ids {
83        center_plain,
84        center_image,
85        right,
86        top_right,
87        top,
88        top_left,
89        left,
90        bottom_left,
91        bottom,
92        bottom_right,
93    }
94}
95
96/// Represents the state of the ImageFrame widget.
97pub struct State {
98    ids: Ids,
99}
100
101impl ImageFrame {
102    builder_methods! {
103        pub edge_src_rects { edge_src_rects = [Option<Rect>; 4] }
104        pub corner_src_rects { corner_src_rects = [Option<Rect>; 4] }
105    }
106
107    pub fn new(
108        edges: [image::Id; 4],
109        corners: [image::Id; 4],
110        center: impl Into<Center>,
111        border_size: impl Into<BorderSize>,
112    ) -> Self {
113        Self {
114            common: widget::CommonBuilder::default(),
115            edges,
116            edge_src_rects: [None; 4],
117            corners,
118            corner_src_rects: [None; 4],
119            center: center.into(),
120            border_size: border_size.into(),
121            color: None,
122        }
123    }
124}
125
126impl Widget for ImageFrame {
127    type Event = ();
128    type State = State;
129    type Style = ();
130
131    fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
132        State {
133            ids: Ids::new(id_gen),
134        }
135    }
136
137    fn style(&self) -> Self::Style {}
138
139    /// Update the state of the ImageFrame
140    fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
141        let widget::UpdateArgs {
142            id,
143            state,
144            rect,
145            ui,
146            ..
147        } = args;
148
149        let (frame_w, frame_h) = rect.w_h();
150
151        let t_height = self.border_size.top.min(frame_h);
152        let b_height = self.border_size.bottom.min(frame_h);
153        let r_width = self.border_size.right.min(frame_w);
154        let l_width = self.border_size.left.min(frame_w);
155        let inner_width = (frame_w - r_width - l_width).max(0.0);
156        let inner_height = (frame_h - t_height - b_height).max(0.0);
157
158        let r_rect = Rect::from_xy_dim([rect.x() + (inner_width + r_width) / 2.0, rect.y()], [
159            r_width,
160            inner_height,
161        ]);
162        let tr_rect = Rect::from_xy_dim(
163            [
164                rect.x() + (inner_width + r_width) / 2.0,
165                rect.y() + (inner_height + t_height) / 2.0,
166            ],
167            [r_width, t_height],
168        );
169        let t_rect = Rect::from_xy_dim([rect.x(), rect.y() + (inner_height + t_height) / 2.0], [
170            inner_width,
171            t_height,
172        ]);
173        let tl_rect = Rect::from_xy_dim(
174            [
175                rect.x() - (inner_width + l_width) / 2.0,
176                rect.y() + (inner_height + t_height) / 2.0,
177            ],
178            [l_width, t_height],
179        );
180        let l_rect = Rect::from_xy_dim([rect.x() - (inner_width + l_width) / 2.0, rect.y()], [
181            l_width,
182            inner_height,
183        ]);
184        let bl_rect = Rect::from_xy_dim(
185            [
186                rect.x() - (inner_width + l_width) / 2.0,
187                rect.y() - (inner_height + b_height) / 2.0,
188            ],
189            [l_width, b_height],
190        );
191        let b_rect = Rect::from_xy_dim([rect.x(), rect.y() - (inner_height + b_height) / 2.0], [
192            inner_width,
193            b_height,
194        ]);
195        let br_rect = Rect::from_xy_dim(
196            [
197                rect.x() + (inner_width + r_width) / 2.0,
198                rect.y() - (inner_height + b_height) / 2.0,
199            ],
200            [r_width, b_height],
201        );
202
203        let maybe_color = self.color;
204        let set_image = |image_id, rect: Rect, maybe_src_rect, widget_id, ui: &mut UiCell| {
205            Image::new(image_id)
206                .xy(rect.xy())
207                .wh(rect.dim())
208                .parent(id)
209                .graphics_for(id)
210                .and_then(maybe_src_rect, |w, r| w.source_rectangle(r))
211                .color(maybe_color)
212                .set(widget_id, ui);
213        };
214        // Right edge
215        set_image(
216            self.edges[2],
217            r_rect,
218            self.edge_src_rects[2],
219            state.ids.right,
220            ui,
221        );
222        // Top-right corner
223        set_image(
224            self.corners[0],
225            tr_rect,
226            self.corner_src_rects[0],
227            state.ids.top_right,
228            ui,
229        );
230        // Top edge
231        set_image(
232            self.edges[0],
233            t_rect,
234            self.edge_src_rects[0],
235            state.ids.top,
236            ui,
237        );
238        // Top-left corner
239        set_image(
240            self.corners[1],
241            tl_rect,
242            self.corner_src_rects[1],
243            state.ids.top_left,
244            ui,
245        );
246        // Left edge
247        set_image(
248            self.edges[3],
249            l_rect,
250            self.edge_src_rects[3],
251            state.ids.left,
252            ui,
253        );
254        // Bottom-left corner
255        set_image(
256            self.corners[3],
257            bl_rect,
258            self.corner_src_rects[3],
259            state.ids.bottom_left,
260            ui,
261        );
262        // Bottom edge
263        set_image(
264            self.edges[1],
265            b_rect,
266            self.edge_src_rects[1],
267            state.ids.bottom,
268            ui,
269        );
270        // Bottom-right corner
271        set_image(
272            self.corners[2],
273            br_rect,
274            self.corner_src_rects[2],
275            state.ids.bottom_right,
276            ui,
277        );
278
279        // Center,
280        match self.center {
281            Center::Plain(color) => {
282                Rectangle::fill_with([inner_width, inner_height], color)
283                    .xy(rect.xy())
284                    .parent(id)
285                    .graphics_for(id)
286                    .and_then(maybe_color, |w, c| {
287                        w.color(color.alpha(match c {
288                            Color::Rgba(_, _, _, a) => a,
289                            Color::Hsla(_, _, _, a) => a,
290                        }))
291                    })
292                    .set(state.ids.center_plain, ui);
293            },
294            Center::Image(image_id, maybe_src_rect) => {
295                set_image(
296                    image_id,
297                    Rect::from_xy_dim(rect.xy(), [inner_width, inner_height]),
298                    maybe_src_rect,
299                    state.ids.center_image,
300                    ui,
301                );
302            },
303        }
304    }
305}
306
307impl Colorable for ImageFrame {
308    fn color(mut self, color: Color) -> Self {
309        self.color = Some(color);
310        self
311    }
312}