diff --git a/egui/src/containers/collapsing_header.rs b/egui/src/containers/collapsing_header.rs index ffbeb40ea..5c6635071 100644 --- a/egui/src/containers/collapsing_header.rs +++ b/egui/src/containers/collapsing_header.rs @@ -154,7 +154,7 @@ impl CollapsingHeader { .text_style(TextStyle::Button) .multiline(false), default_open: false, - id_source : None, + id_source: None, } } diff --git a/egui/src/context.rs b/egui/src/context.rs index 76a8f7ebe..3e8a0d725 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -321,6 +321,7 @@ impl Context { if interaction_id.is_none() || sense == Sense::nothing() { // Not interested in input: return InteractInfo { + sense, rect, hovered, clicked: false, @@ -341,6 +342,7 @@ impl Context { if self.input.mouse.pressed { if hovered { let mut info = InteractInfo { + sense, rect, hovered: true, clicked: false, @@ -368,6 +370,7 @@ impl Context { } else { // miss InteractInfo { + sense, rect, hovered, clicked: false, @@ -378,6 +381,7 @@ impl Context { } else if self.input.mouse.released { let clicked = hovered && active; InteractInfo { + sense, rect, hovered, clicked, @@ -386,6 +390,7 @@ impl Context { } } else if self.input.mouse.down { InteractInfo { + sense, rect, hovered: hovered && active, clicked: false, @@ -394,6 +399,7 @@ impl Context { } } else { InteractInfo { + sense, rect, hovered, clicked: false, diff --git a/egui/src/examples/app.rs b/egui/src/examples/app.rs index 25faf89ca..86523d95b 100644 --- a/egui/src/examples/app.rs +++ b/egui/src/examples/app.rs @@ -274,7 +274,7 @@ impl ExampleWindow { #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", serde(default))] struct Widgets { - checked: bool, + button_enabled: bool, count: usize, radio: usize, slider_value: f32, @@ -285,7 +285,7 @@ struct Widgets { impl Default for Widgets { fn default() -> Self { Self { - checked: true, + button_enabled: true, radio: 0, count: 0, slider_value: 3.14, @@ -305,8 +305,6 @@ impl Widgets { ); }); - ui.add(Checkbox::new(&mut self.checked, "checkbox")); - ui.horizontal(|ui| { if ui.add(radio(self.radio == 0, "First")).clicked { self.radio = 0; @@ -319,9 +317,11 @@ impl Widgets { } }); + ui.add(Checkbox::new(&mut self.button_enabled, "Button enabled")); + ui.inner_layout(Layout::horizontal(Align::Center), |ui| { if ui - .add(Button::new("Click me")) + .add(Button::new("Click me").enabled(self.button_enabled)) .tooltip_text("This will just increase a counter.") .clicked { diff --git a/egui/src/style.rs b/egui/src/style.rs index 3febac6d8..75778516b 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -104,6 +104,7 @@ pub struct Interact { pub active: WidgetStyle, pub hovered: WidgetStyle, pub inactive: WidgetStyle, + pub disabled: WidgetStyle, } impl Default for Interact { @@ -133,13 +134,23 @@ impl Default for Interact { rect_outline: Some(LineStyle::new(1.0, white(128))), corner_radius: 4.0, }, + disabled: WidgetStyle { + bg_fill: None, + fill: srgba(50, 50, 50, 255), + stroke_color: gray(128, 255), // Should look grayed out + stroke_width: 0.5, + rect_outline: Some(LineStyle::new(0.5, white(128))), + corner_radius: 4.0, + }, } } } impl Interact { pub fn style(&self, interact: &InteractInfo) -> &WidgetStyle { - if interact.active { + if interact.sense == Sense::nothing() { + &self.disabled + } else if interact.active { &self.active } else if interact.hovered { &self.hovered diff --git a/egui/src/types.rs b/egui/src/types.rs index 0acbb66b6..73668e6a9 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -41,6 +41,9 @@ impl Default for CursorIcon { #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "with_serde", derive(serde::Serialize))] pub struct InteractInfo { + /// The senses (click or drag) that the widget is interested in (if any). + pub sense: Sense, + /// The mouse is hovering above this thing pub hovered: bool, @@ -59,6 +62,7 @@ pub struct InteractInfo { impl InteractInfo { pub fn nothing() -> Self { Self { + sense: Sense::nothing(), hovered: false, clicked: false, double_clicked: false, @@ -69,6 +73,7 @@ impl InteractInfo { pub fn union(self, other: Self) -> Self { Self { + sense: self.sense.union(other.sense), hovered: self.hovered || other.hovered, clicked: self.clicked || other.clicked, double_clicked: self.double_clicked || other.double_clicked, @@ -82,6 +87,9 @@ impl InteractInfo { // TODO: rename GuiResponse pub struct GuiResponse { + /// The senses (click or drag) that the widget is interested in (if any). + pub sense: Sense, + /// The mouse is hovering above this pub hovered: bool, @@ -120,6 +128,7 @@ impl GuiResponse { impl Into for GuiResponse { fn into(self) -> InteractInfo { InteractInfo { + sense: self.sense, hovered: self.hovered, clicked: self.clicked, double_clicked: self.double_clicked, @@ -133,6 +142,7 @@ impl Into for GuiResponse { /// What sort of interaction is a widget sensitive to? #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "with_serde", derive(serde::Serialize))] pub struct Sense { /// buttons, sliders, windows ... pub click: bool, @@ -170,4 +180,12 @@ impl Sense { drag: true, } } + + #[must_use] + pub fn union(self, other: Self) -> Self { + Self { + click: self.click | other.click, + drag: self.drag | other.drag, + } + } } diff --git a/egui/src/ui.rs b/egui/src/ui.rs index bbf743c4a..0f531fea2 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -303,8 +303,7 @@ impl Ui { &mut self, explicit_id_source: Option, default_id_source: Option<&str>, - ) -> Id - { + ) -> Id { let id = if let Some(explicit_id_source) = explicit_id_source { self.id.with(&explicit_id_source) } else { @@ -315,7 +314,8 @@ impl Ui { self.make_position_id() } }; - self.ctx.register_unique_id(id, default_id_source.unwrap_or_default(), self.cursor) + self.ctx + .register_unique_id(id, default_id_source.unwrap_or_default(), self.cursor) } /// Make an Id that is unique to this positon. @@ -349,12 +349,21 @@ impl Ui { #[must_use] pub fn response(&mut self, interact: InteractInfo) -> GuiResponse { // TODO: unify GuiResponse and InteractInfo. They are the same thing! + let InteractInfo { + sense, + hovered, + clicked, + double_clicked, + active, + rect, + } = interact; GuiResponse { - hovered: interact.hovered, - clicked: interact.clicked, - double_clicked: interact.double_clicked, - active: interact.active, - rect: interact.rect, + sense, + hovered, + clicked, + double_clicked, + active, + rect, ctx: self.ctx.clone(), } } diff --git a/egui/src/widgets.rs b/egui/src/widgets.rs index e135b60d6..53382efb9 100644 --- a/egui/src/widgets.rs +++ b/egui/src/widgets.rs @@ -232,6 +232,15 @@ impl Button { self.sense = sense; 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::nothing())`. + pub fn enabled(mut self, enabled: bool) -> Self { + if !enabled { + self.sense = Sense::nothing(); + } + self + } } impl Widget for Button {