diff --git a/CHANGELOG.md b/CHANGELOG.md index 165c2702..ee3de732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [ * Add resizable panels. * Add an option to overwrite frame of a `Panel`. * Add `ScrollArea::show_rows` for efficient scrolling of huge UI:s. +* Add `Ui::set_visible` as a way to hide widgets. * Add `Style::override_text_style` to easily change the text style of everything in a `Ui` (or globally). * You can now change `TextStyle` on checkboxes, radio buttons and `SelectableLabel`. * Add support for [cint](https://crates.io/crates/cint) under `cint` feature. diff --git a/egui/src/painter.rs b/egui/src/painter.rs index 4afdcaa3..76fd4b6e 100644 --- a/egui/src/painter.rs +++ b/egui/src/painter.rs @@ -66,6 +66,15 @@ impl Painter { self.fade_to_color = fade_to_color; } + pub(crate) fn visible(&self) -> bool { + self.fade_to_color != Some(Color32::TRANSPARENT) + } + + /// If `false`, nothing added to the painter will be visible + pub(crate) fn set_invisible(&mut self) { + self.fade_to_color = Some(Color32::TRANSPARENT) + } + /// Create a painter for a sub-region of this `Painter`. /// /// The clip-rect of the returned `Painter` will be the intersection @@ -145,14 +154,21 @@ impl Painter { /// Can be used for free painting. /// NOTE: all coordinates are screen coordinates! pub fn add(&self, mut shape: Shape) -> ShapeIdx { - self.transform_shape(&mut shape); - self.paint_list.lock().add(self.clip_rect, shape) + if self.fade_to_color == Some(Color32::TRANSPARENT) { + self.paint_list.lock().add(self.clip_rect, Shape::Noop) + } else { + self.transform_shape(&mut shape); + self.paint_list.lock().add(self.clip_rect, shape) + } } /// Add many shapes at once. /// /// Calling this once is generally faster than calling [`Self::add`] multiple times. pub fn extend(&self, mut shapes: Vec) { + if self.fade_to_color == Some(Color32::TRANSPARENT) { + return; + } if !shapes.is_empty() { if self.fade_to_color.is_some() { for shape in &mut shapes { @@ -166,6 +182,9 @@ impl Painter { /// Modify an existing [`Shape`]. pub fn set(&self, idx: ShapeIdx, mut shape: Shape) { + if self.fade_to_color == Some(Color32::TRANSPARENT) { + return; + } self.transform_shape(&mut shape); self.paint_list.lock().set(idx, self.clip_rect, shape) } diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 514abf66..b171c2d3 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -215,14 +215,44 @@ impl Ui { /// ``` pub fn set_enabled(&mut self, enabled: bool) { self.enabled &= enabled; - if self.enabled { - self.painter.set_fade_to_color(None); - } else { + if !self.enabled && self.visible() { self.painter .set_fade_to_color(Some(self.visuals().window_fill())); } } + /// If `false`, any widgets added to the `Ui` will be invisible and non-interactive. + #[inline(always)] + pub fn visible(&self) -> bool { + self.painter.visible() + } + + /// Calling `set_visible(false)` will cause all further widgets to be invisible, + /// yet still allocate space. + /// + /// The widgets will not be interactive (`set_visible(false)` implies `set_enabled(false)`). + /// + /// Calling `set_visible(true)` has no effect. + /// + /// ### Example + /// ``` + /// # let ui = &mut egui::Ui::__test(); + /// # let mut visible = true; + /// ui.group(|ui|{ + /// ui.checkbox(&mut visible, "Show subsection"); + /// ui.set_visible(visible); + /// if ui.button("Button that is not always shown").clicked() { + /// /* … */ + /// } + /// }); + /// ``` + pub fn set_visible(&mut self, visible: bool) { + self.set_enabled(visible); + if !visible { + self.painter.set_invisible(); + } + } + #[inline(always)] pub fn layout(&self) -> &Layout { self.placer.layout() @@ -1226,6 +1256,8 @@ impl Ui { /// ui.label("Within a frame"); /// }); /// ``` + /// + /// Se also [`Self::scope`]. pub fn group(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse { crate::Frame::group(self.style()).show(self, add_contents) } diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index 7570c9e2..8d4f1bea 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -9,6 +9,7 @@ enum Enum { #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] pub struct WidgetGallery { enabled: bool, + visible: bool, boolean: bool, radio: Enum, scalar: f32, @@ -20,6 +21,7 @@ impl Default for WidgetGallery { fn default() -> Self { Self { enabled: true, + visible: true, boolean: false, radio: Enum::First, scalar: 42.0, @@ -47,13 +49,27 @@ impl super::Demo for WidgetGallery { impl super::View for WidgetGallery { fn ui(&mut self, ui: &mut egui::Ui) { - self.gallery(ui); + ui.scope(|ui| { + ui.set_visible(self.visible); + ui.set_enabled(self.enabled); + + egui::Grid::new("my_grid") + .striped(true) + .spacing([40.0, 4.0]) + .show(ui, |ui| { + self.gallery_grid_contents(ui); + }); + }); ui.separator(); - ui.vertical_centered(|ui| { - ui.checkbox(&mut self.enabled, "Interactive") - .on_hover_text("Convenient way to inspect how the widgets look when disabled."); + ui.horizontal(|ui| { + ui.checkbox(&mut self.visible, "Visible") + .on_hover_text("Uncheck to hide all the widgets."); + if self.visible { + ui.checkbox(&mut self.enabled, "Interactive") + .on_hover_text("Uncheck to inspect how the widgets look when disabled."); + } }); ui.separator(); @@ -69,18 +85,10 @@ impl super::View for WidgetGallery { } impl WidgetGallery { - fn gallery(&mut self, ui: &mut egui::Ui) { - egui::Grid::new("my_grid") - .striped(true) - .spacing([40.0, 4.0]) - .show(ui, |ui| { - self.gallery_grid_contents(ui); - }); - } - fn gallery_grid_contents(&mut self, ui: &mut egui::Ui) { let Self { - enabled, + enabled: _, + visible: _, boolean, radio, scalar, @@ -88,8 +96,6 @@ impl WidgetGallery { color, } = self; - ui.set_enabled(*enabled); - ui.add(doc_link_label("Label", "label,heading")); ui.label("Welcome to the widget gallery!"); ui.end_row();