1use 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 edges: [image::Id; 4],
15 edge_src_rects: [Option<Rect>; 4],
16 corners: [image::Id; 4],
18 corner_src_rects: [Option<Rect>; 4],
19 center: Center,
21 border_size: BorderSize,
23 color: Option<Color>,
25 }
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
96pub 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 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 set_image(
216 self.edges[2],
217 r_rect,
218 self.edge_src_rects[2],
219 state.ids.right,
220 ui,
221 );
222 set_image(
224 self.corners[0],
225 tr_rect,
226 self.corner_src_rects[0],
227 state.ids.top_right,
228 ui,
229 );
230 set_image(
232 self.edges[0],
233 t_rect,
234 self.edge_src_rects[0],
235 state.ids.top,
236 ui,
237 );
238 set_image(
240 self.corners[1],
241 tl_rect,
242 self.corner_src_rects[1],
243 state.ids.top_left,
244 ui,
245 );
246 set_image(
248 self.edges[3],
249 l_rect,
250 self.edge_src_rects[3],
251 state.ids.left,
252 ui,
253 );
254 set_image(
256 self.corners[3],
257 bl_rect,
258 self.corner_src_rects[3],
259 state.ids.bottom_left,
260 ui,
261 );
262 set_image(
264 self.edges[1],
265 b_rect,
266 self.edge_src_rects[1],
267 state.ids.bottom,
268 ui,
269 );
270 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 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}