bones_framework/render/ui/widgets/
bordered_frame.rs1use crate::prelude::*;
2
3pub struct BorderedFrame {
5 bg_handle: Handle<Image>,
6 border_scale: f32,
7 texture_size: egui::Vec2,
8 texture_border_size: egui::style::Margin,
9 padding: egui::style::Margin,
10 margin: egui::style::Margin,
11 border_only: bool,
12}
13
14impl BorderedFrame {
15 #[must_use = "You must call .show() to render the frame"]
17 pub fn new(border_image: &BorderImageMeta) -> Self {
18 let s = border_image.image_size;
19 Self {
20 bg_handle: border_image.image,
21 border_scale: border_image.scale,
22 texture_size: egui::Vec2::new(s.x as f32, s.y as f32),
23 texture_border_size: border_image.border_size.into(),
24 padding: Default::default(),
25 margin: Default::default(),
26 border_only: false,
27 }
28 }
29
30 #[must_use = "You must call .show() to render the frame"]
32 pub fn padding(mut self, margin: impl Into<egui::style::Margin>) -> Self {
33 self.padding = margin.into();
34
35 self
36 }
37
38 #[must_use = "You must call .show() to render the frame"]
40 pub fn margin(mut self, margin: egui::style::Margin) -> Self {
41 self.margin = margin;
42
43 self
44 }
45
46 #[allow(unused)] #[must_use = "You must call .show() to render the frame"]
49 pub fn border_scale(mut self, scale: f32) -> Self {
50 self.border_scale = scale;
51
52 self
53 }
54
55 #[allow(unused)] #[must_use = "You must call .show() to render the frame"]
59 pub fn border_only(mut self, border_only: bool) -> Self {
60 self.border_only = border_only;
61
62 self
63 }
64
65 pub fn show<R>(
67 self,
68 ui: &mut egui::Ui,
69 add_contents: impl FnOnce(&mut egui::Ui) -> R,
70 ) -> egui::InnerResponse<R> {
71 self.show_dyn(ui, Box::new(add_contents))
72 }
73
74 fn show_dyn<'c, R>(
75 self,
76 ui: &mut egui::Ui,
77 add_contents: Box<dyn FnOnce(&mut egui::Ui) -> R + 'c>,
78 ) -> egui::InnerResponse<R> {
79 let mut prepared = self.begin(ui);
80 let ret = add_contents(&mut prepared.content_ui);
81 let response = prepared.end(ui);
82
83 egui::InnerResponse {
84 inner: ret,
85 response,
86 }
87 }
88
89 fn begin(self, ui: &mut egui::Ui) -> BorderedFramePrepared {
90 let background_shape_idx = ui.painter().add(egui::Shape::Noop);
91
92 let mut content_rect = ui.available_rect_before_wrap();
93 content_rect.min += self.padding.left_top() + self.margin.left_top();
94 content_rect.max -= self.padding.right_bottom() + self.margin.right_bottom();
95
96 content_rect.max.x = content_rect.max.x.max(content_rect.min.x);
98 content_rect.max.y = content_rect.max.y.max(content_rect.min.y);
99
100 let content_ui = ui.child_ui(content_rect, *ui.layout());
101
102 BorderedFramePrepared {
103 frame: self,
104 background_shape_idx,
105 content_ui,
106 }
107 }
108
109 pub fn paint(&self, texture_id: egui::TextureId, paint_rect: egui::Rect) -> egui::Shape {
111 use egui::{Pos2, Rect, Vec2};
112 let white = egui::Color32::WHITE;
113
114 let mut mesh = egui::Mesh {
115 texture_id,
116 ..Default::default()
117 };
118
119 let s = self.texture_size;
120 let b = self.texture_border_size;
121 let pr = paint_rect;
122 let buv = egui::style::Margin {
124 left: b.left / s.x,
125 right: b.right / s.x,
126 top: b.top / s.y,
127 bottom: b.bottom / s.y,
128 };
129 let b = egui::style::Margin {
130 left: b.left * self.border_scale,
131 right: b.right * self.border_scale,
132 top: b.top * self.border_scale,
133 bottom: b.bottom * self.border_scale,
134 };
135
136 mesh.add_rect_with_uv(
140 Rect::from_min_size(pr.min, Vec2::new(b.left, b.top)),
141 egui::Rect::from_min_size(Pos2::ZERO, Vec2::new(buv.left, buv.top)),
142 white,
143 );
144 mesh.add_rect_with_uv(
146 Rect::from_min_size(
147 pr.min + Vec2::new(b.left, 0.0),
148 Vec2::new(pr.width() - b.left - b.right, b.top),
149 ),
150 egui::Rect::from_min_size(
151 Pos2::new(buv.left, 0.0),
152 Vec2::new(1.0 - buv.left - buv.right, buv.top),
153 ),
154 white,
155 );
156 mesh.add_rect_with_uv(
158 Rect::from_min_size(
159 pr.right_top() - Vec2::new(b.right, 0.0),
160 Vec2::new(b.right, b.top),
161 ),
162 egui::Rect::from_min_size(
163 Pos2::new(1.0 - buv.right, 0.0),
164 Vec2::new(buv.right, buv.top),
165 ),
166 white,
167 );
168 mesh.add_rect_with_uv(
170 Rect::from_min_size(
171 pr.min + Vec2::new(0.0, b.top),
172 Vec2::new(b.left, pr.height() - b.top - b.bottom),
173 ),
174 egui::Rect::from_min_size(
175 Pos2::new(0.0, buv.top),
176 Vec2::new(buv.left, 1.0 - buv.top - buv.bottom),
177 ),
178 white,
179 );
180 if !self.border_only {
182 mesh.add_rect_with_uv(
183 Rect::from_min_size(
184 pr.min + Vec2::new(b.left, b.top),
185 Vec2::new(
186 pr.width() - b.left - b.right,
187 pr.height() - b.top - b.bottom,
188 ),
189 ),
190 egui::Rect::from_min_size(
191 Pos2::new(buv.left, buv.top),
192 Vec2::new(1.0 - buv.left - buv.top, 1.0 - buv.top - buv.bottom),
193 ),
194 white,
195 );
196 }
197 mesh.add_rect_with_uv(
199 Rect::from_min_size(
200 pr.min + Vec2::new(pr.width() - b.right, b.top),
201 Vec2::new(b.right, pr.height() - b.top - b.bottom),
202 ),
203 egui::Rect::from_min_size(
204 Pos2::new(1.0 - buv.right, buv.top),
205 Vec2::new(buv.right, 1.0 - buv.top - buv.bottom),
206 ),
207 white,
208 );
209 mesh.add_rect_with_uv(
211 Rect::from_min_size(
212 pr.min + Vec2::new(0.0, pr.height() - b.bottom),
213 Vec2::new(b.left, b.bottom),
214 ),
215 egui::Rect::from_min_size(
216 Pos2::new(0.0, 1.0 - buv.bottom),
217 Vec2::new(buv.left, buv.bottom),
218 ),
219 white,
220 );
221 mesh.add_rect_with_uv(
223 Rect::from_min_size(
224 pr.min + Vec2::new(b.left, pr.height() - b.bottom),
225 Vec2::new(pr.width() - b.left - b.right, b.bottom),
226 ),
227 egui::Rect::from_min_size(
228 Pos2::new(buv.left, 1.0 - buv.bottom),
229 Vec2::new(1.0 - buv.left - buv.right, buv.bottom),
230 ),
231 white,
232 );
233 mesh.add_rect_with_uv(
235 Rect::from_min_size(
236 pr.min + Vec2::new(pr.width() - b.right, pr.height() - b.bottom),
237 Vec2::new(b.right, b.bottom),
238 ),
239 egui::Rect::from_min_size(
240 Pos2::new(1.0 - buv.right, 1.0 - buv.bottom),
241 Vec2::new(buv.right, buv.bottom),
242 ),
243 white,
244 );
245
246 egui::Shape::Mesh(mesh)
247 }
248}
249
250struct BorderedFramePrepared {
252 frame: BorderedFrame,
253 background_shape_idx: egui::layers::ShapeIdx,
254 content_ui: egui::Ui,
255}
256
257impl BorderedFramePrepared {
258 fn end(self, ui: &mut egui::Ui) -> egui::Response {
259 use egui::Vec2;
260
261 let min_rect = self.content_ui.min_rect();
262 let m = self.frame.padding;
263 let paint_rect = egui::Rect {
264 min: min_rect.min - Vec2::new(m.left, m.top),
265 max: min_rect.max + Vec2::new(m.right, m.bottom),
266 };
267 if ui.is_rect_visible(paint_rect) {
268 let texture = ui.data(|map| {
269 map.get_temp::<AtomicResource<EguiTextures>>(egui::Id::null())
270 .unwrap()
271 .borrow()
272 .unwrap()
273 .get(self.frame.bg_handle)
274 });
275 let shape = self.frame.paint(texture, paint_rect);
276 ui.painter().set(self.background_shape_idx, shape);
277 }
278
279 ui.allocate_rect(paint_rect, egui::Sense::hover())
280 }
281}