1use egui::{
14 Align2, Color32, Context, Frame, Id, InnerResponse, IntoAtoms, Order, Pos2, Rect, Resize, Ui,
15 Vec2, Vec2b, ViewportBuilder, ViewportId, WindowDrag,
16 scroll_area::{DragScroll, ScrollBarVisibility},
17};
18
19#[must_use = "You should call .show()"]
39pub struct Window<'a> {
40 inner: egui::Window<'a>,
41 title_fill: Option<Color32>,
44}
45
46impl<'a> Window<'a> {
47 pub fn new(title: impl IntoAtoms<'a>) -> Self {
50 Self {
51 inner: egui::Window::new(title),
52 title_fill: None,
53 }
54 }
55
56 pub fn from_viewport(id: ViewportId, viewport: ViewportBuilder) -> Self {
58 Self {
59 inner: egui::Window::from_viewport(id, viewport),
60 title_fill: None,
61 }
62 }
63
64 #[inline]
66 pub fn id(mut self, id: Id) -> Self {
67 self.inner = self.inner.id(id);
68 self
69 }
70
71 #[inline]
73 pub fn open(mut self, open: &'a mut bool) -> Self {
74 self.inner = self.inner.open(open);
75 self
76 }
77
78 #[inline]
80 pub fn enabled(mut self, enabled: bool) -> Self {
81 self.inner = self.inner.enabled(enabled);
82 self
83 }
84
85 #[inline]
87 pub fn interactable(mut self, interactable: bool) -> Self {
88 self.inner = self.inner.interactable(interactable);
89 self
90 }
91
92 #[inline]
94 pub fn movable(mut self, movable: bool) -> Self {
95 self.inner = self.inner.movable(movable);
96 self
97 }
98
99 #[inline]
101 pub fn drag_area(mut self, drag_area: WindowDrag) -> Self {
102 self.inner = self.inner.drag_area(drag_area);
103 self
104 }
105
106 #[inline]
108 pub fn order(mut self, order: Order) -> Self {
109 self.inner = self.inner.order(order);
110 self
111 }
112
113 #[inline]
115 pub fn fade_in(mut self, fade_in: bool) -> Self {
116 self.inner = self.inner.fade_in(fade_in);
117 self
118 }
119
120 #[inline]
122 pub fn fade_out(mut self, fade_out: bool) -> Self {
123 self.inner = self.inner.fade_out(fade_out);
124 self
125 }
126
127 #[inline]
129 pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {
130 mutate(&mut self);
131 self
132 }
133
134 #[inline]
136 pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
137 self.inner = self.inner.resize(mutate);
138 self
139 }
140
141 #[inline]
143 pub fn frame(mut self, frame: Frame) -> Self {
144 self.inner = self.inner.frame(frame);
145 self
146 }
147
148 #[inline]
153 pub fn title_frame(mut self, frame: Frame) -> Self {
154 self.title_fill = Some(frame.fill);
155 self.inner = self.inner.title_frame(frame);
156 self
157 }
158
159 #[inline]
161 pub fn min_width(mut self, min_width: f32) -> Self {
162 self.inner = self.inner.min_width(min_width);
163 self
164 }
165
166 #[inline]
168 pub fn min_height(mut self, min_height: f32) -> Self {
169 self.inner = self.inner.min_height(min_height);
170 self
171 }
172
173 #[inline]
175 pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {
176 self.inner = self.inner.min_size(min_size);
177 self
178 }
179
180 #[inline]
182 pub fn max_width(mut self, max_width: f32) -> Self {
183 self.inner = self.inner.max_width(max_width);
184 self
185 }
186
187 #[inline]
189 pub fn max_height(mut self, max_height: f32) -> Self {
190 self.inner = self.inner.max_height(max_height);
191 self
192 }
193
194 #[inline]
196 pub fn max_size(mut self, max_size: impl Into<Vec2>) -> Self {
197 self.inner = self.inner.max_size(max_size);
198 self
199 }
200
201 #[inline]
203 pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {
204 self.inner = self.inner.current_pos(current_pos);
205 self
206 }
207
208 #[inline]
210 pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {
211 self.inner = self.inner.default_pos(default_pos);
212 self
213 }
214
215 #[inline]
217 pub fn fixed_pos(mut self, pos: impl Into<Pos2>) -> Self {
218 self.inner = self.inner.fixed_pos(pos);
219 self
220 }
221
222 #[inline]
224 pub fn constrain(mut self, constrain: bool) -> Self {
225 self.inner = self.inner.constrain(constrain);
226 self
227 }
228
229 #[inline]
231 pub fn constrain_to(mut self, constrain_rect: Rect) -> Self {
232 self.inner = self.inner.constrain_to(constrain_rect);
233 self
234 }
235
236 #[inline]
238 pub fn pivot(mut self, pivot: Align2) -> Self {
239 self.inner = self.inner.pivot(pivot);
240 self
241 }
242
243 #[inline]
245 pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {
246 self.inner = self.inner.anchor(align, offset);
247 self
248 }
249
250 #[inline]
252 pub fn default_open(mut self, default_open: bool) -> Self {
253 self.inner = self.inner.default_open(default_open);
254 self
255 }
256
257 #[inline]
259 pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
260 self.inner = self.inner.default_size(default_size);
261 self
262 }
263
264 #[inline]
266 pub fn default_width(mut self, default_width: f32) -> Self {
267 self.inner = self.inner.default_width(default_width);
268 self
269 }
270
271 #[inline]
273 pub fn default_height(mut self, default_height: f32) -> Self {
274 self.inner = self.inner.default_height(default_height);
275 self
276 }
277
278 #[inline]
280 pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
281 self.inner = self.inner.fixed_size(size);
282 self
283 }
284
285 #[inline]
287 pub fn default_rect(self, rect: Rect) -> Self {
288 self.default_pos(rect.min).default_size(rect.size())
289 }
290
291 #[inline]
293 pub fn fixed_rect(self, rect: Rect) -> Self {
294 self.fixed_pos(rect.min).fixed_size(rect.size())
295 }
296
297 #[inline]
299 pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {
300 self.inner = self.inner.resizable(resizable);
301 self
302 }
303
304 #[inline]
306 pub fn collapsible(mut self, collapsible: bool) -> Self {
307 self.inner = self.inner.collapsible(collapsible);
308 self
309 }
310
311 #[inline]
314 pub fn title_bar(mut self, title_bar: bool) -> Self {
315 self.inner = self.inner.title_bar(title_bar);
316 self
317 }
318
319 #[inline]
322 pub fn auto_sized(mut self) -> Self {
323 self.inner = self.inner.auto_sized();
324 self
325 }
326
327 #[inline]
329 pub fn scroll(mut self, scroll: impl Into<Vec2b>) -> Self {
330 self.inner = self.inner.scroll(scroll);
331 self
332 }
333
334 #[inline]
336 pub fn hscroll(mut self, hscroll: bool) -> Self {
337 self.inner = self.inner.hscroll(hscroll);
338 self
339 }
340
341 #[inline]
343 pub fn vscroll(mut self, vscroll: bool) -> Self {
344 self.inner = self.inner.vscroll(vscroll);
345 self
346 }
347
348 #[inline]
350 pub fn drag_to_scroll(mut self, drag_to_scroll: DragScroll) -> Self {
351 self.inner = self.inner.drag_to_scroll(drag_to_scroll);
352 self
353 }
354
355 #[inline]
357 pub fn scroll_bar_visibility(mut self, visibility: ScrollBarVisibility) -> Self {
358 self.inner = self.inner.scroll_bar_visibility(visibility);
359 self
360 }
361
362 #[inline]
368 pub fn show<R>(
369 self,
370 ctx: &Context,
371 add_contents: impl FnOnce(&mut Ui) -> R,
372 ) -> Option<InnerResponse<Option<R>>> {
373 let fill = self.title_fill.unwrap_or_else(|| ctx.global_style().visuals.window_fill);
374 let _guard = PinOpenWeakFill::new(ctx, fill);
375 self.inner.show(ctx, add_contents)
376 }
377}
378
379struct PinOpenWeakFill {
385 ctx: Context,
386 previous: Color32,
387}
388
389impl PinOpenWeakFill {
390 fn new(ctx: &Context, fill: Color32) -> Self {
391 let previous = ctx.global_style().visuals.widgets.open.weak_bg_fill;
392 ctx.global_style_mut(|style| {
393 style.visuals.widgets.open.weak_bg_fill = fill;
394 });
395 Self {
396 ctx: ctx.clone(),
397 previous,
398 }
399 }
400}
401
402impl Drop for PinOpenWeakFill {
403 fn drop(&mut self) {
404 self.ctx.global_style_mut(|style| {
405 style.visuals.widgets.open.weak_bg_fill = self.previous;
406 });
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 #[test]
415 fn title_frame_fill_is_pinned_during_show_then_restored() {
416 egui::__run_test_ctx(|ctx| {
417 let original = Color32::from_rgb(9, 9, 9);
418 ctx.global_style_mut(|style| {
419 style.visuals.widgets.open.weak_bg_fill = original;
420 });
421
422 let title_fill = Color32::from_rgb(1, 2, 3);
423 Window::new("title").title_frame(Frame::NONE.fill(title_fill)).show(ctx, |ui| {
424 assert_eq!(
425 ui.ctx().global_style().visuals.widgets.open.weak_bg_fill,
426 title_fill
427 );
428 });
429
430 assert_eq!(
431 ctx.global_style().visuals.widgets.open.weak_bg_fill,
432 original
433 );
434 });
435 }
436}