use egui::{
Align2, Color32, Context, Frame, Id, InnerResponse, IntoAtoms, Order, Pos2, Rect, Resize, Ui,
Vec2, Vec2b, ViewportBuilder, ViewportId, WindowDrag,
scroll_area::{DragScroll, ScrollBarVisibility},
};
#[must_use = "You should call .show()"]
pub struct Window<'a> {
inner: egui::Window<'a>,
title_fill: Option<Color32>,
}
impl<'a> Window<'a> {
pub fn new(title: impl IntoAtoms<'a>) -> Self {
Self {
inner: egui::Window::new(title),
title_fill: None,
}
}
pub fn from_viewport(id: ViewportId, viewport: ViewportBuilder) -> Self {
Self {
inner: egui::Window::from_viewport(id, viewport),
title_fill: None,
}
}
#[inline]
pub fn id(mut self, id: Id) -> Self {
self.inner = self.inner.id(id);
self
}
#[inline]
pub fn open(mut self, open: &'a mut bool) -> Self {
self.inner = self.inner.open(open);
self
}
#[inline]
pub fn enabled(mut self, enabled: bool) -> Self {
self.inner = self.inner.enabled(enabled);
self
}
#[inline]
pub fn interactable(mut self, interactable: bool) -> Self {
self.inner = self.inner.interactable(interactable);
self
}
#[inline]
pub fn movable(mut self, movable: bool) -> Self {
self.inner = self.inner.movable(movable);
self
}
#[inline]
pub fn drag_area(mut self, drag_area: WindowDrag) -> Self {
self.inner = self.inner.drag_area(drag_area);
self
}
#[inline]
pub fn order(mut self, order: Order) -> Self {
self.inner = self.inner.order(order);
self
}
#[inline]
pub fn fade_in(mut self, fade_in: bool) -> Self {
self.inner = self.inner.fade_in(fade_in);
self
}
#[inline]
pub fn fade_out(mut self, fade_out: bool) -> Self {
self.inner = self.inner.fade_out(fade_out);
self
}
#[inline]
pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {
mutate(&mut self);
self
}
#[inline]
pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
self.inner = self.inner.resize(mutate);
self
}
#[inline]
pub fn frame(mut self, frame: Frame) -> Self {
self.inner = self.inner.frame(frame);
self
}
#[inline]
pub fn title_frame(mut self, frame: Frame) -> Self {
self.title_fill = Some(frame.fill);
self.inner = self.inner.title_frame(frame);
self
}
#[inline]
pub fn min_width(mut self, min_width: f32) -> Self {
self.inner = self.inner.min_width(min_width);
self
}
#[inline]
pub fn min_height(mut self, min_height: f32) -> Self {
self.inner = self.inner.min_height(min_height);
self
}
#[inline]
pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {
self.inner = self.inner.min_size(min_size);
self
}
#[inline]
pub fn max_width(mut self, max_width: f32) -> Self {
self.inner = self.inner.max_width(max_width);
self
}
#[inline]
pub fn max_height(mut self, max_height: f32) -> Self {
self.inner = self.inner.max_height(max_height);
self
}
#[inline]
pub fn max_size(mut self, max_size: impl Into<Vec2>) -> Self {
self.inner = self.inner.max_size(max_size);
self
}
#[inline]
pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {
self.inner = self.inner.current_pos(current_pos);
self
}
#[inline]
pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {
self.inner = self.inner.default_pos(default_pos);
self
}
#[inline]
pub fn fixed_pos(mut self, pos: impl Into<Pos2>) -> Self {
self.inner = self.inner.fixed_pos(pos);
self
}
#[inline]
pub fn constrain(mut self, constrain: bool) -> Self {
self.inner = self.inner.constrain(constrain);
self
}
#[inline]
pub fn constrain_to(mut self, constrain_rect: Rect) -> Self {
self.inner = self.inner.constrain_to(constrain_rect);
self
}
#[inline]
pub fn pivot(mut self, pivot: Align2) -> Self {
self.inner = self.inner.pivot(pivot);
self
}
#[inline]
pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {
self.inner = self.inner.anchor(align, offset);
self
}
#[inline]
pub fn default_open(mut self, default_open: bool) -> Self {
self.inner = self.inner.default_open(default_open);
self
}
#[inline]
pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
self.inner = self.inner.default_size(default_size);
self
}
#[inline]
pub fn default_width(mut self, default_width: f32) -> Self {
self.inner = self.inner.default_width(default_width);
self
}
#[inline]
pub fn default_height(mut self, default_height: f32) -> Self {
self.inner = self.inner.default_height(default_height);
self
}
#[inline]
pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
self.inner = self.inner.fixed_size(size);
self
}
#[inline]
pub fn default_rect(self, rect: Rect) -> Self {
self.default_pos(rect.min).default_size(rect.size())
}
#[inline]
pub fn fixed_rect(self, rect: Rect) -> Self {
self.fixed_pos(rect.min).fixed_size(rect.size())
}
#[inline]
pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {
self.inner = self.inner.resizable(resizable);
self
}
#[inline]
pub fn collapsible(mut self, collapsible: bool) -> Self {
self.inner = self.inner.collapsible(collapsible);
self
}
#[inline]
pub fn title_bar(mut self, title_bar: bool) -> Self {
self.inner = self.inner.title_bar(title_bar);
self
}
#[inline]
pub fn auto_sized(mut self) -> Self {
self.inner = self.inner.auto_sized();
self
}
#[inline]
pub fn scroll(mut self, scroll: impl Into<Vec2b>) -> Self {
self.inner = self.inner.scroll(scroll);
self
}
#[inline]
pub fn hscroll(mut self, hscroll: bool) -> Self {
self.inner = self.inner.hscroll(hscroll);
self
}
#[inline]
pub fn vscroll(mut self, vscroll: bool) -> Self {
self.inner = self.inner.vscroll(vscroll);
self
}
#[inline]
pub fn drag_to_scroll(mut self, drag_to_scroll: DragScroll) -> Self {
self.inner = self.inner.drag_to_scroll(drag_to_scroll);
self
}
#[inline]
pub fn scroll_bar_visibility(mut self, visibility: ScrollBarVisibility) -> Self {
self.inner = self.inner.scroll_bar_visibility(visibility);
self
}
#[inline]
pub fn show<R>(
self,
ctx: &Context,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> Option<InnerResponse<Option<R>>> {
let fill = self
.title_fill
.unwrap_or_else(|| ctx.global_style().visuals.window_fill);
let _guard = PinOpenWeakFill::new(ctx, fill);
self.inner.show(ctx, add_contents)
}
}
struct PinOpenWeakFill {
ctx: Context,
previous: Color32,
}
impl PinOpenWeakFill {
fn new(ctx: &Context, fill: Color32) -> Self {
let previous = ctx.global_style().visuals.widgets.open.weak_bg_fill;
ctx.global_style_mut(|style| {
style.visuals.widgets.open.weak_bg_fill = fill;
});
Self {
ctx: ctx.clone(),
previous,
}
}
}
impl Drop for PinOpenWeakFill {
fn drop(&mut self) {
self.ctx.global_style_mut(|style| {
style.visuals.widgets.open.weak_bg_fill = self.previous;
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn title_frame_fill_is_pinned_during_show_then_restored() {
egui::__run_test_ctx(|ctx| {
let original = Color32::from_rgb(9, 9, 9);
ctx.global_style_mut(|style| {
style.visuals.widgets.open.weak_bg_fill = original;
});
let title_fill = Color32::from_rgb(1, 2, 3);
Window::new("title")
.title_frame(Frame::NONE.fill(title_fill))
.show(ctx, |ui| {
assert_eq!(
ui.ctx().global_style().visuals.widgets.open.weak_bg_fill,
title_fill
);
});
assert_eq!(
ctx.global_style().visuals.widgets.open.weak_bg_fill,
original
);
});
}
}