From a7eed0ae3c1c97bf5d85477aeade2eff0d342895 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 30 May 2024 13:23:01 +0200 Subject: [PATCH] Once you have waited for a tooltip to show, show the next one right away (#4585) * Closes https://github.com/emilk/egui/issues/4582 User-story: there are multiple icons in a row, and the user wants them explained. They hover over one until the tooltips appears. They then move on to the next and don't want to wait for that tooltip again. --- crates/egui/src/response.rs | 61 +++++++++++++++++++++++++------------ crates/egui/src/style.rs | 22 ++++++++++++- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index 21bcf31bc04..ac6f13db998 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -584,14 +584,7 @@ impl Response { return true; } - if self.context_menu_opened() { - return false; - } - - if ComboBox::is_open(&self.ctx, self.id) { - return false; // Don't cover the open ComboBox with a tooltip - } - + // Fast early-outs: if self.enabled { if !self.hovered || !self.ctx.input(|i| i.pointer.has_pointer()) { return false; @@ -600,20 +593,44 @@ impl Response { return false; } - if self.ctx.style().interaction.show_tooltips_only_when_still { - // We only show the tooltip when the mouse pointer is still, - // but once shown we keep showing it until the mouse leaves the parent. + if self.context_menu_opened() { + return false; + } - if !self.ctx.input(|i| i.pointer.is_still()) && !self.is_tooltip_open() { - // wait for mouse to stop - self.ctx.request_repaint(); - return false; - } + if ComboBox::is_open(&self.ctx, self.id) { + return false; // Don't cover the open ComboBox with a tooltip } - if !self.is_tooltip_open() { - let time_til_tooltip = self.ctx.style().interaction.tooltip_delay - - self.ctx.input(|i| i.pointer.time_since_last_movement()); + let when_was_a_toolip_last_shown_id = Id::new("when_was_a_toolip_last_shown"); + let now = self.ctx.input(|i| i.time); + + let when_was_a_toolip_last_shown = self + .ctx + .data(|d| d.get_temp::(when_was_a_toolip_last_shown_id)); + + let tooltip_delay = self.ctx.style().interaction.tooltip_delay; + let tooltip_grace_time = self.ctx.style().interaction.tooltip_grace_time; + + // There is a tooltip_delay before showing the first tooltip, + // but once one tooltips is show, moving the mouse cursor to + // another widget should show the tooltip for that widget right away. + + // Let the user quickly move over some dead space to hover the next thing + let tooltip_was_recently_shown = when_was_a_toolip_last_shown + .map_or(false, |time| ((now - time) as f32) < tooltip_grace_time); + + if !tooltip_was_recently_shown && !self.is_tooltip_open() { + if self.ctx.style().interaction.show_tooltips_only_when_still { + // We only show the tooltip when the mouse pointer is still. + if !self.ctx.input(|i| i.pointer.is_still()) { + // wait for mouse to stop + self.ctx.request_repaint(); + return false; + } + } + + let time_til_tooltip = + tooltip_delay - self.ctx.input(|i| i.pointer.time_since_last_movement()); if 0.0 < time_til_tooltip { // Wait until the mouse has been still for a while @@ -633,6 +650,12 @@ impl Response { return false; } + // All checks passed: show the tooltip! + + // Remember that we're showing a tooltip + self.ctx + .data_mut(|data| data.insert_temp::(when_was_a_toolip_last_shown_id, now)); + true } diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index c04624623c3..fd1c13a8aab 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -658,6 +658,13 @@ pub struct Interaction { /// Delay in seconds before showing tooltips after the mouse stops moving pub tooltip_delay: f32, + /// If you have waited for a tooltip and then hover some other widget within + /// this many seconds, then show the new tooltip right away, + /// skipping [`Self::tooltip_delay`]. + /// + /// This lets the user quickly move over some dead space to hover the next thing. + pub tooltip_grace_time: f32, + /// Can you select the text on a [`crate::Label`] by default? pub selectable_labels: bool, @@ -1102,7 +1109,8 @@ impl Default for Interaction { resize_grab_radius_corner: 10.0, interact_radius: 5.0, show_tooltips_only_when_still: true, - tooltip_delay: 0.3, + tooltip_delay: 0.5, + tooltip_grace_time: 0.2, selectable_labels: true, multi_widget_text_select: true, } @@ -1590,6 +1598,7 @@ impl Interaction { resize_grab_radius_corner, show_tooltips_only_when_still, tooltip_delay, + tooltip_grace_time, selectable_labels, multi_widget_text_select, } = self; @@ -1623,6 +1632,17 @@ impl Interaction { .suffix(" s"), ); ui.end_row(); + + ui.label("Tooltip grace time").on_hover_text( + "If a tooltip is open and you hover another widget within this grace period, show the next tooltip right away", + ); + ui.add( + DragValue::new(tooltip_grace_time) + .clamp_range(0.0..=1.0) + .speed(0.05) + .suffix(" s"), + ); + ui.end_row(); }); ui.checkbox(