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()"]
43pub struct Window<'a> {
44 inner: egui::Window<'a>,
45 title_fill: Option<Color32>,
48}
49
50impl<'a> Window<'a> {
51 pub fn new(title: impl IntoAtoms<'a>) -> Self {
54 Self {
55 inner: egui::Window::new(title),
56 title_fill: None,
57 }
58 }
59
60 pub fn from_viewport(id: ViewportId, viewport: ViewportBuilder) -> Self {
62 Self {
63 inner: egui::Window::from_viewport(id, viewport),
64 title_fill: None,
65 }
66 }
67
68 #[inline]
70 pub fn id(mut self, id: Id) -> Self {
71 self.inner = self.inner.id(id);
72 self
73 }
74
75 #[inline]
77 pub fn open(mut self, open: &'a mut bool) -> Self {
78 self.inner = self.inner.open(open);
79 self
80 }
81
82 #[inline]
84 pub fn enabled(mut self, enabled: bool) -> Self {
85 self.inner = self.inner.enabled(enabled);
86 self
87 }
88
89 #[inline]
91 pub fn interactable(mut self, interactable: bool) -> Self {
92 self.inner = self.inner.interactable(interactable);
93 self
94 }
95
96 #[inline]
98 pub fn movable(mut self, movable: bool) -> Self {
99 self.inner = self.inner.movable(movable);
100 self
101 }
102
103 #[inline]
105 pub fn drag_area(mut self, drag_area: WindowDrag) -> Self {
106 self.inner = self.inner.drag_area(drag_area);
107 self
108 }
109
110 #[inline]
112 pub fn order(mut self, order: Order) -> Self {
113 self.inner = self.inner.order(order);
114 self
115 }
116
117 #[inline]
119 pub fn fade_in(mut self, fade_in: bool) -> Self {
120 self.inner = self.inner.fade_in(fade_in);
121 self
122 }
123
124 #[inline]
126 pub fn fade_out(mut self, fade_out: bool) -> Self {
127 self.inner = self.inner.fade_out(fade_out);
128 self
129 }
130
131 #[inline]
133 pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {
134 mutate(&mut self);
135 self
136 }
137
138 #[inline]
140 pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
141 self.inner = self.inner.resize(mutate);
142 self
143 }
144
145 #[inline]
147 pub fn frame(mut self, frame: Frame) -> Self {
148 self.inner = self.inner.frame(frame);
149 self
150 }
151
152 #[inline]
157 pub fn title_frame(mut self, frame: Frame) -> Self {
158 self.title_fill = Some(frame.fill);
159 self.inner = self.inner.title_frame(frame);
160 self
161 }
162
163 #[inline]
165 pub fn min_width(mut self, min_width: f32) -> Self {
166 self.inner = self.inner.min_width(min_width);
167 self
168 }
169
170 #[inline]
172 pub fn min_height(mut self, min_height: f32) -> Self {
173 self.inner = self.inner.min_height(min_height);
174 self
175 }
176
177 #[inline]
179 pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {
180 self.inner = self.inner.min_size(min_size);
181 self
182 }
183
184 #[inline]
186 pub fn max_width(mut self, max_width: f32) -> Self {
187 self.inner = self.inner.max_width(max_width);
188 self
189 }
190
191 #[inline]
193 pub fn max_height(mut self, max_height: f32) -> Self {
194 self.inner = self.inner.max_height(max_height);
195 self
196 }
197
198 #[inline]
200 pub fn max_size(mut self, max_size: impl Into<Vec2>) -> Self {
201 self.inner = self.inner.max_size(max_size);
202 self
203 }
204
205 #[inline]
207 pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {
208 self.inner = self.inner.current_pos(current_pos);
209 self
210 }
211
212 #[inline]
214 pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {
215 self.inner = self.inner.default_pos(default_pos);
216 self
217 }
218
219 #[inline]
221 pub fn fixed_pos(mut self, pos: impl Into<Pos2>) -> Self {
222 self.inner = self.inner.fixed_pos(pos);
223 self
224 }
225
226 #[inline]
228 pub fn constrain(mut self, constrain: bool) -> Self {
229 self.inner = self.inner.constrain(constrain);
230 self
231 }
232
233 #[inline]
235 pub fn constrain_to(mut self, constrain_rect: Rect) -> Self {
236 self.inner = self.inner.constrain_to(constrain_rect);
237 self
238 }
239
240 #[inline]
242 pub fn pivot(mut self, pivot: Align2) -> Self {
243 self.inner = self.inner.pivot(pivot);
244 self
245 }
246
247 #[inline]
249 pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {
250 self.inner = self.inner.anchor(align, offset);
251 self
252 }
253
254 #[inline]
256 pub fn default_open(mut self, default_open: bool) -> Self {
257 self.inner = self.inner.default_open(default_open);
258 self
259 }
260
261 #[inline]
263 pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
264 self.inner = self.inner.default_size(default_size);
265 self
266 }
267
268 #[inline]
270 pub fn default_width(mut self, default_width: f32) -> Self {
271 self.inner = self.inner.default_width(default_width);
272 self
273 }
274
275 #[inline]
277 pub fn default_height(mut self, default_height: f32) -> Self {
278 self.inner = self.inner.default_height(default_height);
279 self
280 }
281
282 #[inline]
284 pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
285 self.inner = self.inner.fixed_size(size);
286 self
287 }
288
289 #[inline]
291 pub fn default_rect(self, rect: Rect) -> Self {
292 self.default_pos(rect.min).default_size(rect.size())
293 }
294
295 #[inline]
297 pub fn fixed_rect(self, rect: Rect) -> Self {
298 self.fixed_pos(rect.min).fixed_size(rect.size())
299 }
300
301 #[inline]
303 pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {
304 self.inner = self.inner.resizable(resizable);
305 self
306 }
307
308 #[inline]
310 pub fn collapsible(mut self, collapsible: bool) -> Self {
311 self.inner = self.inner.collapsible(collapsible);
312 self
313 }
314
315 #[inline]
318 pub fn title_bar(mut self, title_bar: bool) -> Self {
319 self.inner = self.inner.title_bar(title_bar);
320 self
321 }
322
323 #[inline]
326 pub fn auto_sized(mut self) -> Self {
327 self.inner = self.inner.auto_sized();
328 self
329 }
330
331 #[inline]
333 pub fn scroll(mut self, scroll: impl Into<Vec2b>) -> Self {
334 self.inner = self.inner.scroll(scroll);
335 self
336 }
337
338 #[inline]
340 pub fn hscroll(mut self, hscroll: bool) -> Self {
341 self.inner = self.inner.hscroll(hscroll);
342 self
343 }
344
345 #[inline]
347 pub fn vscroll(mut self, vscroll: bool) -> Self {
348 self.inner = self.inner.vscroll(vscroll);
349 self
350 }
351
352 #[inline]
354 pub fn drag_to_scroll(mut self, drag_to_scroll: DragScroll) -> Self {
355 self.inner = self.inner.drag_to_scroll(drag_to_scroll);
356 self
357 }
358
359 #[inline]
361 pub fn scroll_bar_visibility(mut self, visibility: ScrollBarVisibility) -> Self {
362 self.inner = self.inner.scroll_bar_visibility(visibility);
363 self
364 }
365
366 #[inline]
372 pub fn show<R>(
373 self,
374 ctx: &Context,
375 add_contents: impl FnOnce(&mut Ui) -> R,
376 ) -> Option<InnerResponse<Option<R>>> {
377 let fill = self.title_fill.unwrap_or_else(|| ctx.global_style().visuals.window_fill);
378 let _guard = PinOpenWeakFill::new(ctx, fill);
379 self.inner.show(ctx, add_contents)
380 }
381}
382
383struct PinOpenWeakFill {
389 ctx: Context,
390 previous: Color32,
391}
392
393impl PinOpenWeakFill {
394 fn new(ctx: &Context, fill: Color32) -> Self {
395 let previous = ctx.global_style().visuals.widgets.open.weak_bg_fill;
396 ctx.global_style_mut(|style| {
397 style.visuals.widgets.open.weak_bg_fill = fill;
398 });
399 Self {
400 ctx: ctx.clone(),
401 previous,
402 }
403 }
404}
405
406impl Drop for PinOpenWeakFill {
407 fn drop(&mut self) {
408 self.ctx.global_style_mut(|style| {
409 style.visuals.widgets.open.weak_bg_fill = self.previous;
410 });
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn title_frame_fill_is_pinned_during_show_then_restored() {
420 egui::__run_test_ctx(|ctx| {
421 let original = Color32::from_rgb(9, 9, 9);
422 ctx.global_style_mut(|style| {
423 style.visuals.widgets.open.weak_bg_fill = original;
424 });
425
426 let title_fill = Color32::from_rgb(1, 2, 3);
427 Window::new("title").title_frame(Frame::NONE.fill(title_fill)).show(ctx, |ui| {
428 assert_eq!(
429 ui.ctx().global_style().visuals.widgets.open.weak_bg_fill,
430 title_fill
431 );
432 });
433
434 assert_eq!(
435 ctx.global_style().visuals.widgets.open.weak_bg_fill,
436 original
437 );
438 });
439 }
440}