Add `Ui::set_opacity` (#3965)
Closes <https://github.com/emilk/egui/issues/3473>.
This commit is contained in:
parent
34e8af87d4
commit
2feb1440be
|
|
@ -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 {
|
||||
|
|
@ -38,6 +43,7 @@ impl Painter {
|
|||
layer_id,
|
||||
clip_rect,
|
||||
fade_to_color: None,
|
||||
opacity_factor: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +55,7 @@ impl Painter {
|
|||
layer_id,
|
||||
clip_rect: self.clip_rect,
|
||||
fade_to_color: None,
|
||||
opacity_factor: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
@ -170,10 +187,10 @@ impl Painter {
|
|||
///
|
||||
/// Calling this once is generally faster than calling [`Self::add`] multiple times.
|
||||
pub fn extend<I: IntoIterator<Item = Shape>>(&self, shapes: I) {
|
||||
if self.fade_to_color == Some(Color32::TRANSPARENT) {
|
||||
if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {
|
||||
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
|
||||
|
|
@ -181,7 +198,7 @@ impl Painter {
|
|||
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 +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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue