diff --git a/CHANGELOG.md b/CHANGELOG.md index a5bc5a64..f69d01c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Add horizontal scrolling support to `ScrollArea` and `Window` (opt-in). * `TextEdit::layouter`: Add custom text layout for e.g. syntax highlighting or WYSIWYG. * `Fonts::layout_job`: New text layout engine allowing mixing fonts, colors and styles, with underlining and strikethrough. +* Add `ui.add_enabled(bool, widget)` to easily add a possibly disabled widget. +* Add `ui.add_enabled_ui(bool, |ui| …)` to create a possibly disabled UI section. * Add feature `"serialize"` separatedly from `"persistence"`. * Add `egui::widgets::global_dark_light_mode_buttons` to easily add buttons for switching the egui theme. * `TextEdit` can now be used to show text which can be selectedd and copied, but not edited. @@ -35,6 +37,9 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Show tooltips above widgets on touch screens. * Fix popups sometimes getting clipped by panels. +### Removed 🔥 +* Replace `Button::enabled` with `ui.add_enabled`. + ## 0.14.2 - 2021-08-28 - Window resize fix diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 7d7f7d28..c7fc56ab 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -207,6 +207,11 @@ impl Ui { /// If `false`, the `Ui` does not allow any interaction and /// the widgets in it will draw with a gray look. #[inline(always)] + pub fn is_enabled(&self) -> bool { + self.enabled + } + + #[deprecated = "Renamed to is_enabled"] pub fn enabled(&self) -> bool { self.enabled } @@ -214,6 +219,8 @@ impl Ui { /// Calling `set_enabled(false)` will cause the `Ui` to deny all future interaction /// and all the widgets will draw with a gray look. /// + /// Usually it is more convenient to use [`Self::add_enabled_ui`] or [`Self::add_enabled`]. + /// /// Calling `set_enabled(true)` has no effect - it will NOT re-enable the `Ui` once disabled. /// /// ### Example @@ -926,6 +933,59 @@ impl Ui { .inner } + /// Add a single[`Widget`] that is possibly disabled, i.e. greyed out and non-interactive. + /// + /// If you call `add_enabled` from within an already disabled UI, + /// the widget will always be disabled, even if the `enabled` argument is true. + /// + /// See also [`Self::add_enabled_ui`] and [`Self::is_enabled`]. + /// + /// ``` + /// # let ui = &mut egui::Ui::__test(); + /// ui.add_enabled(false, egui::Button::new("Can't click this")); + /// ``` + pub fn add_enabled(&mut self, enabled: bool, widget: impl Widget) -> Response { + if enabled || !self.is_enabled() { + self.add(widget) + } else { + let old_painter = self.painter.clone(); + self.set_enabled(false); + let response = self.add(widget); + self.enabled = true; + self.painter = old_painter; + response + } + } + + /// Add a section that is possibly disabled, i.e. greyed out and non-interactive. + /// + /// If you call `add_enabled_ui` from within an already disabled UI, + /// the result will always be disabled, even if the `enabled` argument is true. + /// + /// See also [`Self::add_enabled`] and [`Self::is_enabled`]. + /// + /// ### Example + /// ``` + /// # let ui = &mut egui::Ui::__test(); + /// # let mut enabled = true; + /// ui.checkbox(&mut enabled, "Enable subsection"); + /// ui.add_enabled_ui(enabled, |ui| { + /// if ui.button("Button that is not always clickable").clicked() { + /// /* … */ + /// } + /// }); + /// ``` + pub fn add_enabled_ui( + &mut self, + enabled: bool, + add_contents: impl FnOnce(&mut Ui) -> R, + ) -> InnerResponse { + self.scope(|ui| { + ui.set_enabled(enabled); + add_contents(ui) + }) + } + /// Add extra space before the next widget. /// /// The direction is dependent on the layout. diff --git a/egui/src/widgets/button.rs b/egui/src/widgets/button.rs index 8ee787e3..00b5f4bb 100644 --- a/egui/src/widgets/button.rs +++ b/egui/src/widgets/button.rs @@ -15,10 +15,16 @@ fn select(b: bool, if_true: T, if_false: T) -> T { /// /// ``` /// # let ui = &mut egui::Ui::__test(); +/// # fn do_stuff() {} +/// /// if ui.add(egui::Button::new("Click mew")).clicked() { /// do_stuff(); /// } -/// # fn do_stuff() {} +/// +/// // A greyed-out and non-interactive button: +/// if ui.add_enabled(false, egui::Button::new("Can't click this")).clicked() { +/// unreachable!(); +/// } /// ``` #[must_use = "You should put this widget in an ui with `ui.add(widget);`"] pub struct Button { @@ -103,17 +109,6 @@ impl Button { self } - /// If you set this to `false`, the button will be grayed out and un-clickable. - /// `enabled(false)` has the same effect as calling `sense(Sense::hover())`. - /// - /// This is a convenience for [`Ui::set_enabled`]. - pub fn enabled(mut self, enabled: bool) -> Self { - if !enabled { - self.sense = Sense::hover(); - } - self - } - /// If `true`, the text will wrap at the `max_width`. /// By default [`Self::wrap`] will be true in vertical layouts /// and horizontal layouts with wrapping, @@ -131,8 +126,8 @@ impl Button { } } -impl Button { - fn enabled_ui(self, ui: &mut Ui) -> Response { +impl Widget for Button { + fn ui(self, ui: &mut Ui) -> Response { let Button { text, text_color, @@ -201,22 +196,6 @@ impl Button { } } -impl Widget for Button { - fn ui(self, ui: &mut Ui) -> Response { - let button_enabled = self.sense != Sense::hover(); - if button_enabled || !ui.enabled() { - self.enabled_ui(ui) - } else { - // We need get a temporary disabled `Ui` to get that grayed out look: - ui.scope(|ui| { - ui.set_enabled(false); - self.enabled_ui(ui) - }) - .inner - } - } -} - // ---------------------------------------------------------------------------- // TODO: allow checkbox without a text label diff --git a/egui/src/widgets/mod.rs b/egui/src/widgets/mod.rs index 98a65026..74901a38 100644 --- a/egui/src/widgets/mod.rs +++ b/egui/src/widgets/mod.rs @@ -91,7 +91,7 @@ pub fn reset_button(ui: &mut Ui, value: &mut T) { /// The button is only enabled if the value does not already have its original value. pub fn reset_button_with(ui: &mut Ui, value: &mut T, reset_value: T) { if ui - .add(Button::new("Reset").enabled(*value != reset_value)) + .add_enabled(*value != reset_value, Button::new("Reset")) .clicked() { *value = reset_value; diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index a582689b..5d280a94 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -53,9 +53,8 @@ impl super::Demo for WidgetGallery { impl super::View for WidgetGallery { fn ui(&mut self, ui: &mut egui::Ui) { - ui.scope(|ui| { + ui.add_enabled_ui(self.enabled, |ui| { ui.set_visible(self.visible); - ui.set_enabled(self.enabled); egui::Grid::new("my_grid") .num_columns(2) diff --git a/egui_demo_lib/src/backend_panel.rs b/egui_demo_lib/src/backend_panel.rs index 9fb175e4..b9cee6be 100644 --- a/egui_demo_lib/src/backend_panel.rs +++ b/egui_demo_lib/src/backend_panel.rs @@ -240,10 +240,9 @@ impl BackendPanel { ) .on_hover_text("Physical pixels per point."); if let Some(native_pixels_per_point) = info.native_pixels_per_point { - let button = egui::Button::new("Reset") - .enabled(*pixels_per_point != native_pixels_per_point); + let enabled = *pixels_per_point != native_pixels_per_point; if ui - .add(button) + .add_enabled(enabled, egui::Button::new("Reset")) .on_hover_text(format!( "Reset scale to native value ({:.1})", native_pixels_per_point diff --git a/egui_demo_lib/src/syntax_highlighting.rs b/egui_demo_lib/src/syntax_highlighting.rs index f1a48414..a4b0238a 100644 --- a/egui_demo_lib/src/syntax_highlighting.rs +++ b/egui_demo_lib/src/syntax_highlighting.rs @@ -262,7 +262,7 @@ impl CodeTheme { }; if ui - .add(egui::Button::new("Reset theme").enabled(*self != reset_value)) + .add_enabled(*self != reset_value, egui::Button::new("Reset theme")) .clicked() { *self = reset_value;