From 23cc06804caef369c5b04341814c94f3ce2dee4a Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Sun, 4 Feb 2024 21:19:16 +0100 Subject: [PATCH] Add Ui::set_opacity Closes #3473 --- crates/egui/src/painter.rs | 44 ++++++++++++++----- crates/egui/src/ui.rs | 20 +++++++++ .../egui_demo_lib/src/demo/widget_gallery.rs | 10 +++++ 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index e318fbcd621..f10bf270399 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -28,6 +28,11 @@ pub struct Painter { /// If set, all shapes will have their colors modified to be closer to this. /// This is used to implement grayed out interfaces. fade_to_color: Option, + + /// If set, all shapes will have their colors modified with [`Color32::gamma_multiply`] with + /// this value as the factor. + /// This is used to make interfaces semi-transparent. + opacity: Option, } impl Painter { @@ -38,6 +43,7 @@ impl Painter { layer_id, clip_rect, fade_to_color: None, + opacity: None, } } @@ -49,6 +55,7 @@ impl Painter { layer_id, clip_rect: self.clip_rect, fade_to_color: None, + opacity: None, } } @@ -62,6 +69,7 @@ impl Painter { layer_id: self.layer_id, clip_rect: rect.intersect(self.clip_rect), fade_to_color: self.fade_to_color, + opacity: self.opacity, } } @@ -75,6 +83,10 @@ impl Painter { self.fade_to_color = fade_to_color; } + pub(crate) fn set_opacity(&mut self, opacity: Option) { + self.opacity = opacity.filter(|f| f.is_finite()).map(|f| f.clamp(0.0, 1.0)); + } + pub(crate) fn is_visible(&self) -> bool { self.fade_to_color != Some(Color32::TRANSPARENT) } @@ -151,6 +163,9 @@ impl Painter { if let Some(fade_to_color) = self.fade_to_color { tint_shape_towards(shape, fade_to_color); } + if let Some(opacity) = self.opacity { + set_shape_opacity(shape, opacity); + } } /// It is up to the caller to make sure there is room for this. @@ -170,18 +185,17 @@ impl Painter { /// /// Calling this once is generally faster than calling [`Self::add`] multiple times. pub fn extend>(&self, shapes: I) { - if self.fade_to_color == Some(Color32::TRANSPARENT) { - return; + match (self.fade_to_color, self.opacity) { + (None, None) => self.paint_list(|l| l.extend(self.clip_rect, shapes)), + (Some(Color32::TRANSPARENT), _) => (), + (_, Some(_)) | (Some(_), _) => { + let shapes = shapes.into_iter().map(|mut shape| { + self.transform_shape(&mut shape); + shape + }); + self.paint_list(|l| l.extend(self.clip_rect, shapes)); + } } - if self.fade_to_color.is_some() { - let shapes = shapes.into_iter().map(|mut shape| { - self.transform_shape(&mut shape); - shape - }); - self.paint_list(|l| l.extend(self.clip_rect, shapes)); - } else { - self.paint_list(|l| l.extend(self.clip_rect, shapes)); - }; } /// Modify an existing [`Shape`]. @@ -496,3 +510,11 @@ fn tint_shape_towards(shape: &mut Shape, target: Color32) { } }); } + +fn set_shape_opacity(shape: &mut Shape, opacity: f32) { + epaint::shape_transform::adjust_colors(shape, &|color| { + if *color != Color32::PLACEHOLDER { + *color = color.gamma_multiply(opacity); + } + }); +} diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 559711a25e1..ab71c46f564 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -277,6 +277,26 @@ impl Ui { } } + /// Make the widget in this [`Ui`] semi-transparent. + /// + /// `opacity` must be between 0.0 and 1.0, where 0.0 means fully transparent (i.e., invisible) + /// and 1.0 means fully opaque (i.e., the same as not calling the method at all). + /// + /// ### Example + /// ``` + /// # egui::__run_test_ui(|ui| { + /// ui.group(|ui| { + /// ui.set_opacity(0.5); + /// if ui.button("Half-transparent button").clicked() { + /// /* … */ + /// } + /// }); + /// # }); + /// ``` + pub fn set_opacity(&mut self, opacity: f32) { + self.painter.set_opacity(Some(opacity)); + } + /// Read the [`Layout`]. #[inline] pub fn layout(&self) -> &Layout { diff --git a/crates/egui_demo_lib/src/demo/widget_gallery.rs b/crates/egui_demo_lib/src/demo/widget_gallery.rs index 4189e61c9ff..ac55a4d01e1 100644 --- a/crates/egui_demo_lib/src/demo/widget_gallery.rs +++ b/crates/egui_demo_lib/src/demo/widget_gallery.rs @@ -12,6 +12,7 @@ pub struct WidgetGallery { enabled: bool, visible: bool, boolean: bool, + opacity: f32, radio: Enum, scalar: f32, string: String, @@ -28,6 +29,7 @@ impl Default for WidgetGallery { Self { enabled: true, visible: true, + opacity: 1.0, boolean: false, radio: Enum::First, scalar: 42.0, @@ -61,6 +63,7 @@ impl super::View for WidgetGallery { fn ui(&mut self, ui: &mut egui::Ui) { ui.add_enabled_ui(self.enabled, |ui| { ui.set_visible(self.visible); + ui.set_opacity(self.opacity); egui::Grid::new("my_grid") .num_columns(2) @@ -79,6 +82,12 @@ impl super::View for WidgetGallery { if self.visible { ui.checkbox(&mut self.enabled, "Interactive") .on_hover_text("Uncheck to inspect how the widgets look when disabled."); + (ui.add( + egui::DragValue::new(&mut self.opacity) + .speed(0.01) + .clamp_range(0.0..=1.0), + ) | ui.label("Opacity")) + .on_hover_text("Reduce this value to make widgets semi-transparent"); } }); @@ -99,6 +108,7 @@ impl WidgetGallery { let Self { enabled: _, visible: _, + opacity: _, boolean, radio, scalar,