diff --git a/CHANGELOG.md b/CHANGELOG.md index eb1bb945..4a593543 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [ ## Unreleased +### Added ⭐ +* Make labels interactive with `Label::sense(Sense::click())`. ## 0.11.0 - 2021-04-05 - Optimization, screen reader & new layout logic diff --git a/egui/src/response.rs b/egui/src/response.rs index 438dbad5..789402b4 100644 --- a/egui/src/response.rs +++ b/egui/src/response.rs @@ -105,6 +105,11 @@ impl std::fmt::Debug for Response { impl Response { /// Returns true if this widget was clicked this frame by the primary button. + /// + /// Note that the widget must be sensing clicks with [`Sense::click`]. + /// [`crate::Button`] senses clicks; [`crate::Label`] does not (unless you call [`crate::Label::sense`]). + /// + /// You can use [`Self::interact`] to sense more things *after* adding a widget. #[inline(always)] pub fn clicked(&self) -> bool { self.clicked[PointerButton::Primary as usize] @@ -198,6 +203,11 @@ impl Response { /// /// To find out which button(s), query [`crate::PointerState::button_down`] /// (`ui.input().pointer.button_down(…)`). + /// + /// Note that the widget must be sensing drags with [`Sense::drag`]. + /// [`crate::DragValue`] senses drags; [`crate::Label`] does not (unless you call [`crate::Label::sense`]). + /// + /// You can use [`Self::interact`] to sense more things *after* adding a widget. #[inline(always)] pub fn dragged(&self) -> bool { self.dragged @@ -340,10 +350,13 @@ impl Response { /// Check for more interactions (e.g. sense clicks on a `Response` returned from a label). /// + /// Note that this call will not add any hover-effects to the widget, so when possible + /// it is better to give the widget a `Sense` instead, e.g. using `[Label::sense]`. + /// /// ``` /// # let mut ui = egui::Ui::__test(); /// let response = ui.label("hello"); - /// assert!(!response.clicked()); // labels don't sense clicks + /// assert!(!response.clicked()); // labels don't sense clicks by default /// let response = response.interact(egui::Sense::click()); /// if response.clicked() { /* … */ } /// ``` diff --git a/egui/src/sense.rs b/egui/src/sense.rs index 27ef9322..6e7d3a8d 100644 --- a/egui/src/sense.rs +++ b/egui/src/sense.rs @@ -75,4 +75,9 @@ impl Sense { focusable: self.focusable | other.focusable, } } + + /// Returns true if we sense either clicks or drags. + pub fn interactive(&self) -> bool { + self.click || self.drag + } } diff --git a/egui/src/style.rs b/egui/src/style.rs index 7f8c7dfa..01f96d92 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -243,7 +243,9 @@ pub struct Widgets { impl Widgets { pub fn style(&self, response: &Response) -> &WidgetVisuals { - if response.is_pointer_button_down_on() || response.has_focus() { + if !response.sense.interactive() { + &self.noninteractive + } else if response.is_pointer_button_down_on() || response.has_focus() { &self.active } else if response.hovered() { &self.hovered diff --git a/egui/src/widgets/label.rs b/egui/src/widgets/label.rs index 7dedc0b0..19afcd2f 100644 --- a/egui/src/widgets/label.rs +++ b/egui/src/widgets/label.rs @@ -25,6 +25,7 @@ pub struct Label { underline: bool, italics: bool, raised: bool, + sense: Sense, } impl Label { @@ -42,6 +43,7 @@ impl Label { underline: false, italics: false, raised: false, + sense: Sense::focusable_noninteractive(), } } @@ -141,6 +143,24 @@ impl Label { self.text_color = Some(text_color.into()); self } + + /// Make the label response to clicks and/or drags. + /// + /// By default, a label is inert and does not response to click or drags. + /// By calling this you can turn the label into a button of sorts. + /// This will also give the label the hover-effect of a button, but without the frame. + /// + /// ``` rust + /// # use egui::{Label, Sense}; + /// # let ui = &mut egui::Ui::__test(); + /// if ui.add(Label::new("click me").sense(Sense::click())).clicked() { + /// /* … */ + /// } + /// ``` + pub fn sense(mut self, sense: Sense) -> Self { + self.sense = sense; + self + } } impl Label { @@ -176,10 +196,17 @@ impl Label { // This should be the easiest method of putting text anywhere. pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: Arc) { - self.paint_galley_focus(ui, pos, galley, false) + self.paint_galley_impl(ui, pos, galley, false, ui.visuals().text_color()) } - fn paint_galley_focus(&self, ui: &mut Ui, pos: Pos2, galley: Arc, focus: bool) { + fn paint_galley_impl( + &self, + ui: &mut Ui, + pos: Pos2, + galley: Arc, + has_focus: bool, + response_color: Color32, + ) { let Self { mut background_color, code, @@ -192,7 +219,7 @@ impl Label { .. } = *self; - let underline = underline || focus; + let underline = underline || has_focus; let text_color = if let Some(text_color) = self.text_color { text_color @@ -201,7 +228,7 @@ impl Label { } else if weak { ui.visuals().weak_text_color() } else { - ui.visuals().text_color() + response_color }; if code { @@ -287,7 +314,7 @@ impl Label { impl Widget for Label { fn ui(self, ui: &mut Ui) -> Response { - let sense = Sense::focusable_noninteractive(); + let sense = self.sense; if self.should_wrap(ui) && ui.layout().main_dir() == Direction::LeftToRight @@ -339,13 +366,15 @@ impl Widget for Label { response |= ui.allocate_rect(rect, sense); } response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, &galley.text)); - self.paint_galley_focus(ui, pos, galley, response.has_focus()); + let response_color = ui.style().interact(&response).text_color(); + self.paint_galley_impl(ui, pos, galley, response.has_focus(), response_color); response } else { let galley = self.layout(ui); let (rect, response) = ui.allocate_exact_size(galley.size, sense); response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, &galley.text)); - self.paint_galley_focus(ui, rect.min, galley, response.has_focus()); + let response_color = ui.style().interact(&response).text_color(); + self.paint_galley_impl(ui, rect.min, galley, response.has_focus(), response_color); response } }