Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ui::set_opacity #3965

Merged
merged 5 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions crates/egui/src/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Color32>,

/// 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_factor: f32,
}

impl Painter {
Expand All @@ -38,6 +43,7 @@ impl Painter {
layer_id,
clip_rect,
fade_to_color: None,
opacity_factor: 1.0,
}
}

Expand All @@ -49,6 +55,7 @@ impl Painter {
layer_id,
clip_rect: self.clip_rect,
fade_to_color: None,
opacity_factor: 1.0,
}
}

Expand All @@ -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_factor: self.opacity_factor,
}
}

Expand All @@ -75,6 +83,12 @@ impl Painter {
self.fade_to_color = fade_to_color;
}

pub(crate) fn set_opacity(&mut self, opacity: f32) {
if opacity.is_finite() {
self.opacity_factor = opacity.clamp(0.0, 1.0);
}
}

pub(crate) fn is_visible(&self) -> bool {
self.fade_to_color != Some(Color32::TRANSPARENT)
}
Expand Down Expand Up @@ -151,13 +165,16 @@ impl Painter {
if let Some(fade_to_color) = self.fade_to_color {
tint_shape_towards(shape, fade_to_color);
}
if self.opacity_factor < 1.0 {
multiply_opacity(shape, self.opacity_factor);
}
}

/// It is up to the caller to make sure there is room for this.
/// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates!
pub fn add(&self, shape: impl Into<Shape>) -> ShapeIdx {
if self.fade_to_color == Some(Color32::TRANSPARENT) {
if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {
self.paint_list(|l| l.add(self.clip_rect, Shape::Noop))
} else {
let mut shape = shape.into();
Expand All @@ -170,18 +187,18 @@ impl Painter {
///
/// Calling this once is generally faster than calling [`Self::add`] multiple times.
pub fn extend<I: IntoIterator<Item = Shape>>(&self, shapes: I) {
YgorSouza marked this conversation as resolved.
Show resolved Hide resolved
if self.fade_to_color == Some(Color32::TRANSPARENT) {
if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {
YgorSouza marked this conversation as resolved.
Show resolved Hide resolved
return;
}
if self.fade_to_color.is_some() {
if self.fade_to_color.is_some() || self.opacity_factor < 1.0 {
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`].
Expand Down Expand Up @@ -496,3 +513,11 @@ fn tint_shape_towards(shape: &mut Shape, target: Color32) {
}
});
}

fn multiply_opacity(shape: &mut Shape, opacity: f32) {
epaint::shape_transform::adjust_colors(shape, &|color| {
if *color != Color32::PLACEHOLDER {
*color = color.gamma_multiply(opacity);
}
});
}
20 changes: 20 additions & 0 deletions crates/egui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(opacity);
}

/// Read the [`Layout`].
#[inline]
pub fn layout(&self) -> &Layout {
Expand Down
10 changes: 10 additions & 0 deletions crates/egui_demo_lib/src/demo/widget_gallery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct WidgetGallery {
enabled: bool,
visible: bool,
boolean: bool,
opacity: f32,
radio: Enum,
scalar: f32,
string: String,
Expand All @@ -28,6 +29,7 @@ impl Default for WidgetGallery {
Self {
enabled: true,
visible: true,
opacity: 1.0,
boolean: false,
radio: Enum::First,
scalar: 42.0,
Expand Down Expand Up @@ -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)
Expand All @@ -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");
}
});

Expand All @@ -99,6 +108,7 @@ impl WidgetGallery {
let Self {
enabled: _,
visible: _,
opacity: _,
boolean,
radio,
scalar,
Expand Down
Loading