Add Label::sense so you can make clickable labels

relates to https://github.com/emilk/egui/issues/292
This commit is contained in:
Emil Ernerfeldt 2021-04-12 21:26:13 +02:00
parent 3c0c729af8
commit 5d50fa1350
5 changed files with 60 additions and 9 deletions

View file

@ -7,6 +7,8 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [
## Unreleased ## Unreleased
### Added ⭐
* Make labels interactive with `Label::sense(Sense::click())`.
## 0.11.0 - 2021-04-05 - Optimization, screen reader & new layout logic ## 0.11.0 - 2021-04-05 - Optimization, screen reader & new layout logic

View file

@ -105,6 +105,11 @@ impl std::fmt::Debug for Response {
impl Response { impl Response {
/// Returns true if this widget was clicked this frame by the primary button. /// 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)] #[inline(always)]
pub fn clicked(&self) -> bool { pub fn clicked(&self) -> bool {
self.clicked[PointerButton::Primary as usize] self.clicked[PointerButton::Primary as usize]
@ -198,6 +203,11 @@ impl Response {
/// ///
/// To find out which button(s), query [`crate::PointerState::button_down`] /// To find out which button(s), query [`crate::PointerState::button_down`]
/// (`ui.input().pointer.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)] #[inline(always)]
pub fn dragged(&self) -> bool { pub fn dragged(&self) -> bool {
self.dragged self.dragged
@ -340,10 +350,13 @@ impl Response {
/// Check for more interactions (e.g. sense clicks on a `Response` returned from a label). /// 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 mut ui = egui::Ui::__test();
/// let response = ui.label("hello"); /// 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()); /// let response = response.interact(egui::Sense::click());
/// if response.clicked() { /* … */ } /// if response.clicked() { /* … */ }
/// ``` /// ```

View file

@ -75,4 +75,9 @@ impl Sense {
focusable: self.focusable | other.focusable, focusable: self.focusable | other.focusable,
} }
} }
/// Returns true if we sense either clicks or drags.
pub fn interactive(&self) -> bool {
self.click || self.drag
}
} }

View file

@ -243,7 +243,9 @@ pub struct Widgets {
impl Widgets { impl Widgets {
pub fn style(&self, response: &Response) -> &WidgetVisuals { 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 &self.active
} else if response.hovered() { } else if response.hovered() {
&self.hovered &self.hovered

View file

@ -25,6 +25,7 @@ pub struct Label {
underline: bool, underline: bool,
italics: bool, italics: bool,
raised: bool, raised: bool,
sense: Sense,
} }
impl Label { impl Label {
@ -42,6 +43,7 @@ impl Label {
underline: false, underline: false,
italics: false, italics: false,
raised: false, raised: false,
sense: Sense::focusable_noninteractive(),
} }
} }
@ -141,6 +143,24 @@ impl Label {
self.text_color = Some(text_color.into()); self.text_color = Some(text_color.into());
self 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 { impl Label {
@ -176,10 +196,17 @@ impl Label {
// This should be the easiest method of putting text anywhere. // This should be the easiest method of putting text anywhere.
pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: Arc<Galley>) { pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: Arc<Galley>) {
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<Galley>, focus: bool) { fn paint_galley_impl(
&self,
ui: &mut Ui,
pos: Pos2,
galley: Arc<Galley>,
has_focus: bool,
response_color: Color32,
) {
let Self { let Self {
mut background_color, mut background_color,
code, code,
@ -192,7 +219,7 @@ impl Label {
.. ..
} = *self; } = *self;
let underline = underline || focus; let underline = underline || has_focus;
let text_color = if let Some(text_color) = self.text_color { let text_color = if let Some(text_color) = self.text_color {
text_color text_color
@ -201,7 +228,7 @@ impl Label {
} else if weak { } else if weak {
ui.visuals().weak_text_color() ui.visuals().weak_text_color()
} else { } else {
ui.visuals().text_color() response_color
}; };
if code { if code {
@ -287,7 +314,7 @@ impl Label {
impl Widget for Label { impl Widget for Label {
fn ui(self, ui: &mut Ui) -> Response { fn ui(self, ui: &mut Ui) -> Response {
let sense = Sense::focusable_noninteractive(); let sense = self.sense;
if self.should_wrap(ui) if self.should_wrap(ui)
&& ui.layout().main_dir() == Direction::LeftToRight && ui.layout().main_dir() == Direction::LeftToRight
@ -339,13 +366,15 @@ impl Widget for Label {
response |= ui.allocate_rect(rect, sense); response |= ui.allocate_rect(rect, sense);
} }
response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, &galley.text)); 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 response
} else { } else {
let galley = self.layout(ui); let galley = self.layout(ui);
let (rect, response) = ui.allocate_exact_size(galley.size, sense); let (rect, response) = ui.allocate_exact_size(galley.size, sense);
response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, &galley.text)); 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 response
} }
} }