Skip to content

Commit 2feb144

Browse files
authored
Add Ui::set_opacity (#3965)
Closes <#3473>.
1 parent 34e8af8 commit 2feb144

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

crates/egui/src/painter.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ pub struct Painter {
2828
/// If set, all shapes will have their colors modified to be closer to this.
2929
/// This is used to implement grayed out interfaces.
3030
fade_to_color: Option<Color32>,
31+
32+
/// If set, all shapes will have their colors modified with [`Color32::gamma_multiply`] with
33+
/// this value as the factor.
34+
/// This is used to make interfaces semi-transparent.
35+
opacity_factor: f32,
3136
}
3237

3338
impl Painter {
@@ -38,6 +43,7 @@ impl Painter {
3843
layer_id,
3944
clip_rect,
4045
fade_to_color: None,
46+
opacity_factor: 1.0,
4147
}
4248
}
4349

@@ -49,6 +55,7 @@ impl Painter {
4955
layer_id,
5056
clip_rect: self.clip_rect,
5157
fade_to_color: None,
58+
opacity_factor: 1.0,
5259
}
5360
}
5461

@@ -62,6 +69,7 @@ impl Painter {
6269
layer_id: self.layer_id,
6370
clip_rect: rect.intersect(self.clip_rect),
6471
fade_to_color: self.fade_to_color,
72+
opacity_factor: self.opacity_factor,
6573
}
6674
}
6775

@@ -75,6 +83,12 @@ impl Painter {
7583
self.fade_to_color = fade_to_color;
7684
}
7785

86+
pub(crate) fn set_opacity(&mut self, opacity: f32) {
87+
if opacity.is_finite() {
88+
self.opacity_factor = opacity.clamp(0.0, 1.0);
89+
}
90+
}
91+
7892
pub(crate) fn is_visible(&self) -> bool {
7993
self.fade_to_color != Some(Color32::TRANSPARENT)
8094
}
@@ -151,13 +165,16 @@ impl Painter {
151165
if let Some(fade_to_color) = self.fade_to_color {
152166
tint_shape_towards(shape, fade_to_color);
153167
}
168+
if self.opacity_factor < 1.0 {
169+
multiply_opacity(shape, self.opacity_factor);
170+
}
154171
}
155172

156173
/// It is up to the caller to make sure there is room for this.
157174
/// Can be used for free painting.
158175
/// NOTE: all coordinates are screen coordinates!
159176
pub fn add(&self, shape: impl Into<Shape>) -> ShapeIdx {
160-
if self.fade_to_color == Some(Color32::TRANSPARENT) {
177+
if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {
161178
self.paint_list(|l| l.add(self.clip_rect, Shape::Noop))
162179
} else {
163180
let mut shape = shape.into();
@@ -170,18 +187,18 @@ impl Painter {
170187
///
171188
/// Calling this once is generally faster than calling [`Self::add`] multiple times.
172189
pub fn extend<I: IntoIterator<Item = Shape>>(&self, shapes: I) {
173-
if self.fade_to_color == Some(Color32::TRANSPARENT) {
190+
if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {
174191
return;
175192
}
176-
if self.fade_to_color.is_some() {
193+
if self.fade_to_color.is_some() || self.opacity_factor < 1.0 {
177194
let shapes = shapes.into_iter().map(|mut shape| {
178195
self.transform_shape(&mut shape);
179196
shape
180197
});
181198
self.paint_list(|l| l.extend(self.clip_rect, shapes));
182199
} else {
183200
self.paint_list(|l| l.extend(self.clip_rect, shapes));
184-
};
201+
}
185202
}
186203

187204
/// Modify an existing [`Shape`].
@@ -496,3 +513,11 @@ fn tint_shape_towards(shape: &mut Shape, target: Color32) {
496513
}
497514
});
498515
}
516+
517+
fn multiply_opacity(shape: &mut Shape, opacity: f32) {
518+
epaint::shape_transform::adjust_colors(shape, &|color| {
519+
if *color != Color32::PLACEHOLDER {
520+
*color = color.gamma_multiply(opacity);
521+
}
522+
});
523+
}

crates/egui/src/ui.rs

+20
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,26 @@ impl Ui {
277277
}
278278
}
279279

280+
/// Make the widget in this [`Ui`] semi-transparent.
281+
///
282+
/// `opacity` must be between 0.0 and 1.0, where 0.0 means fully transparent (i.e., invisible)
283+
/// and 1.0 means fully opaque (i.e., the same as not calling the method at all).
284+
///
285+
/// ### Example
286+
/// ```
287+
/// # egui::__run_test_ui(|ui| {
288+
/// ui.group(|ui| {
289+
/// ui.set_opacity(0.5);
290+
/// if ui.button("Half-transparent button").clicked() {
291+
/// /* … */
292+
/// }
293+
/// });
294+
/// # });
295+
/// ```
296+
pub fn set_opacity(&mut self, opacity: f32) {
297+
self.painter.set_opacity(opacity);
298+
}
299+
280300
/// Read the [`Layout`].
281301
#[inline]
282302
pub fn layout(&self) -> &Layout {

crates/egui_demo_lib/src/demo/widget_gallery.rs

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct WidgetGallery {
1212
enabled: bool,
1313
visible: bool,
1414
boolean: bool,
15+
opacity: f32,
1516
radio: Enum,
1617
scalar: f32,
1718
string: String,
@@ -28,6 +29,7 @@ impl Default for WidgetGallery {
2829
Self {
2930
enabled: true,
3031
visible: true,
32+
opacity: 1.0,
3133
boolean: false,
3234
radio: Enum::First,
3335
scalar: 42.0,
@@ -61,6 +63,7 @@ impl super::View for WidgetGallery {
6163
fn ui(&mut self, ui: &mut egui::Ui) {
6264
ui.add_enabled_ui(self.enabled, |ui| {
6365
ui.set_visible(self.visible);
66+
ui.set_opacity(self.opacity);
6467

6568
egui::Grid::new("my_grid")
6669
.num_columns(2)
@@ -79,6 +82,12 @@ impl super::View for WidgetGallery {
7982
if self.visible {
8083
ui.checkbox(&mut self.enabled, "Interactive")
8184
.on_hover_text("Uncheck to inspect how the widgets look when disabled.");
85+
(ui.add(
86+
egui::DragValue::new(&mut self.opacity)
87+
.speed(0.01)
88+
.clamp_range(0.0..=1.0),
89+
) | ui.label("Opacity"))
90+
.on_hover_text("Reduce this value to make widgets semi-transparent");
8291
}
8392
});
8493

@@ -99,6 +108,7 @@ impl WidgetGallery {
99108
let Self {
100109
enabled: _,
101110
visible: _,
111+
opacity: _,
102112
boolean,
103113
radio,
104114
scalar,

0 commit comments

Comments
 (0)