parent
143119943d
commit
977d83a08f
|
|
@ -1442,6 +1442,16 @@ impl Context {
|
||||||
self.request_repaint_after_for(duration, self.viewport_id());
|
self.request_repaint_after_for(duration, self.viewport_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Repaint after this many seconds.
|
||||||
|
///
|
||||||
|
/// See [`Self::request_repaint_after`] for details.
|
||||||
|
#[track_caller]
|
||||||
|
pub fn request_repaint_after_secs(&self, seconds: f32) {
|
||||||
|
if let Ok(duration) = std::time::Duration::try_from_secs_f32(seconds) {
|
||||||
|
self.request_repaint_after(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Request repaint after at most the specified duration elapses.
|
/// Request repaint after at most the specified duration elapses.
|
||||||
///
|
///
|
||||||
/// The backend can chose to repaint sooner, for instance if some other code called
|
/// The backend can chose to repaint sooner, for instance if some other code called
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,12 @@ pub struct InputState {
|
||||||
/// (We keep a separate [`TouchState`] for each encountered touch device.)
|
/// (We keep a separate [`TouchState`] for each encountered touch device.)
|
||||||
touch_states: BTreeMap<TouchDeviceId, TouchState>,
|
touch_states: BTreeMap<TouchDeviceId, TouchState>,
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// Scrolling:
|
||||||
|
//
|
||||||
|
/// Time of the last scroll event.
|
||||||
|
last_scroll_time: f64,
|
||||||
|
|
||||||
/// Used for smoothing the scroll delta.
|
/// Used for smoothing the scroll delta.
|
||||||
unprocessed_scroll_delta: Vec2,
|
unprocessed_scroll_delta: Vec2,
|
||||||
|
|
||||||
|
|
@ -87,6 +93,7 @@ pub struct InputState {
|
||||||
/// * `zoom > 1`: pinch spread
|
/// * `zoom > 1`: pinch spread
|
||||||
zoom_factor_delta: f32,
|
zoom_factor_delta: f32,
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
/// Position and size of the egui area.
|
/// Position and size of the egui area.
|
||||||
pub screen_rect: Rect,
|
pub screen_rect: Rect,
|
||||||
|
|
||||||
|
|
@ -161,11 +168,14 @@ impl Default for InputState {
|
||||||
raw: Default::default(),
|
raw: Default::default(),
|
||||||
pointer: Default::default(),
|
pointer: Default::default(),
|
||||||
touch_states: Default::default(),
|
touch_states: Default::default(),
|
||||||
|
|
||||||
|
last_scroll_time: f64::NEG_INFINITY,
|
||||||
unprocessed_scroll_delta: Vec2::ZERO,
|
unprocessed_scroll_delta: Vec2::ZERO,
|
||||||
unprocessed_scroll_delta_for_zoom: 0.0,
|
unprocessed_scroll_delta_for_zoom: 0.0,
|
||||||
raw_scroll_delta: Vec2::ZERO,
|
raw_scroll_delta: Vec2::ZERO,
|
||||||
smooth_scroll_delta: Vec2::ZERO,
|
smooth_scroll_delta: Vec2::ZERO,
|
||||||
zoom_factor_delta: 1.0,
|
zoom_factor_delta: 1.0,
|
||||||
|
|
||||||
screen_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
|
screen_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
|
||||||
pixels_per_point: 1.0,
|
pixels_per_point: 1.0,
|
||||||
max_texture_side: 2048,
|
max_texture_side: 2048,
|
||||||
|
|
@ -320,14 +330,24 @@ impl InputState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_scrolling = raw_scroll_delta != Vec2::ZERO || smooth_scroll_delta != Vec2::ZERO;
|
||||||
|
let last_scroll_time = if is_scrolling {
|
||||||
|
time
|
||||||
|
} else {
|
||||||
|
self.last_scroll_time
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pointer,
|
pointer,
|
||||||
touch_states: self.touch_states,
|
touch_states: self.touch_states,
|
||||||
|
|
||||||
|
last_scroll_time,
|
||||||
unprocessed_scroll_delta,
|
unprocessed_scroll_delta,
|
||||||
unprocessed_scroll_delta_for_zoom,
|
unprocessed_scroll_delta_for_zoom,
|
||||||
raw_scroll_delta,
|
raw_scroll_delta,
|
||||||
smooth_scroll_delta,
|
smooth_scroll_delta,
|
||||||
zoom_factor_delta,
|
zoom_factor_delta,
|
||||||
|
|
||||||
screen_rect,
|
screen_rect,
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
|
max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
|
||||||
|
|
@ -393,6 +413,12 @@ impl InputState {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How long has it been (in seconds) since the use last scrolled?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn time_since_last_scroll(&self) -> f32 {
|
||||||
|
(self.time - self.last_scroll_time) as f32
|
||||||
|
}
|
||||||
|
|
||||||
/// The [`crate::Context`] will call this at the end of each frame to see if we need a repaint.
|
/// The [`crate::Context`] will call this at the end of each frame to see if we need a repaint.
|
||||||
///
|
///
|
||||||
/// Returns how long to wait for a repaint.
|
/// Returns how long to wait for a repaint.
|
||||||
|
|
@ -1218,6 +1244,7 @@ impl InputState {
|
||||||
pointer,
|
pointer,
|
||||||
touch_states,
|
touch_states,
|
||||||
|
|
||||||
|
last_scroll_time,
|
||||||
unprocessed_scroll_delta,
|
unprocessed_scroll_delta,
|
||||||
unprocessed_scroll_delta_for_zoom,
|
unprocessed_scroll_delta_for_zoom,
|
||||||
raw_scroll_delta,
|
raw_scroll_delta,
|
||||||
|
|
@ -1257,6 +1284,10 @@ impl InputState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.label(format!(
|
||||||
|
"Time since last scroll: {:.1} s",
|
||||||
|
time - last_scroll_time
|
||||||
|
));
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"unprocessed_scroll_delta: {unprocessed_scroll_delta:?} points"
|
"unprocessed_scroll_delta: {unprocessed_scroll_delta:?} points"
|
||||||
|
|
@ -1270,6 +1301,7 @@ impl InputState {
|
||||||
"smooth_scroll_delta: {smooth_scroll_delta:?} points"
|
"smooth_scroll_delta: {smooth_scroll_delta:?} points"
|
||||||
));
|
));
|
||||||
ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
|
ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
|
||||||
|
|
||||||
ui.label(format!("screen_rect: {screen_rect:?} points"));
|
ui.label(format!("screen_rect: {screen_rect:?} points"));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"{pixels_per_point} physical pixels for each logical point"
|
"{pixels_per_point} physical pixels for each logical point"
|
||||||
|
|
|
||||||
|
|
@ -611,6 +611,21 @@ impl Response {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let style = self.ctx.style();
|
||||||
|
|
||||||
|
let tooltip_delay = style.interaction.tooltip_delay;
|
||||||
|
let tooltip_grace_time = style.interaction.tooltip_grace_time;
|
||||||
|
|
||||||
|
let time_since_last_scroll = self.ctx.input(|i| i.time_since_last_scroll());
|
||||||
|
|
||||||
|
if time_since_last_scroll < tooltip_delay {
|
||||||
|
// See https://github.com/emilk/egui/issues/4781
|
||||||
|
// Note that this means we cannot have `ScrollArea`s in a tooltip.
|
||||||
|
self.ctx
|
||||||
|
.request_repaint_after_secs(tooltip_delay - time_since_last_scroll);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let is_our_tooltip_open = self.is_tooltip_open();
|
let is_our_tooltip_open = self.is_tooltip_open();
|
||||||
|
|
||||||
if is_our_tooltip_open {
|
if is_our_tooltip_open {
|
||||||
|
|
@ -680,9 +695,6 @@ impl Response {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
// There is a tooltip_delay before showing the first tooltip,
|
||||||
// but once one tooltips is show, moving the mouse cursor to
|
// but once one tooltips is show, moving the mouse cursor to
|
||||||
// another widget should show the tooltip for that widget right away.
|
// another widget should show the tooltip for that widget right away.
|
||||||
|
|
@ -692,23 +704,27 @@ impl Response {
|
||||||
crate::popup::seconds_since_last_tooltip(&self.ctx) < tooltip_grace_time;
|
crate::popup::seconds_since_last_tooltip(&self.ctx) < tooltip_grace_time;
|
||||||
|
|
||||||
if !tooltip_was_recently_shown && !is_our_tooltip_open {
|
if !tooltip_was_recently_shown && !is_our_tooltip_open {
|
||||||
if self.ctx.style().interaction.show_tooltips_only_when_still {
|
if style.interaction.show_tooltips_only_when_still {
|
||||||
// We only show the tooltip when the mouse pointer is still.
|
// We only show the tooltip when the mouse pointer is still.
|
||||||
if !self.ctx.input(|i| i.pointer.is_still()) {
|
if !self
|
||||||
|
.ctx
|
||||||
|
.input(|i| i.pointer.is_still() && i.smooth_scroll_delta == Vec2::ZERO)
|
||||||
|
{
|
||||||
// wait for mouse to stop
|
// wait for mouse to stop
|
||||||
self.ctx.request_repaint();
|
self.ctx.request_repaint();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_til_tooltip =
|
let time_since_last_interaction = self.ctx.input(|i| {
|
||||||
tooltip_delay - self.ctx.input(|i| i.pointer.time_since_last_movement());
|
i.time_since_last_scroll()
|
||||||
|
.max(i.pointer.time_since_last_movement())
|
||||||
|
});
|
||||||
|
let time_til_tooltip = tooltip_delay - time_since_last_interaction;
|
||||||
|
|
||||||
if 0.0 < time_til_tooltip {
|
if 0.0 < time_til_tooltip {
|
||||||
// Wait until the mouse has been still for a while
|
// Wait until the mouse has been still for a while
|
||||||
if let Ok(duration) = std::time::Duration::try_from_secs_f32(time_til_tooltip) {
|
self.ctx.request_repaint_after_secs(time_til_tooltip);
|
||||||
self.ctx.request_repaint_after(duration);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,7 @@ pub fn paint_text_cursor(
|
||||||
total_duration - time_in_cycle
|
total_duration - time_in_cycle
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.ctx()
|
ui.ctx().request_repaint_after_secs(wake_in);
|
||||||
.request_repaint_after(std::time::Duration::from_secs_f32(wake_in));
|
|
||||||
} else {
|
} else {
|
||||||
paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
|
paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ impl crate::Demo for Tooltips {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
let window = egui::Window::new("Tooltips")
|
let window = egui::Window::new("Tooltips")
|
||||||
.constrain(false) // So we can test how tooltips behave close to the screen edge
|
.constrain(false) // So we can test how tooltips behave close to the screen edge
|
||||||
.resizable(false)
|
.resizable(true)
|
||||||
|
.default_size([450.0, 300.0])
|
||||||
.scroll(false)
|
.scroll(false)
|
||||||
.open(open);
|
.open(open);
|
||||||
window.show(ctx, |ui| self.ui(ui));
|
window.show(ctx, |ui| self.ui(ui));
|
||||||
|
|
@ -34,6 +35,34 @@ impl crate::View for Tooltips {
|
||||||
ui.add(crate::egui_github_link_file_line!());
|
ui.add(crate::egui_github_link_file_line!());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
egui::SidePanel::right("scroll_test").show_inside(ui, |ui| {
|
||||||
|
ui.label(
|
||||||
|
"The scroll area below has many labels with interactive tooltips. \
|
||||||
|
The purpose is to test that the tooltips close when you scroll.",
|
||||||
|
)
|
||||||
|
.on_hover_text("Try hovering a label below, then scroll!");
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.auto_shrink(false)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
for i in 0..1000 {
|
||||||
|
ui.label(format!("This is line {i}")).on_hover_ui(|ui| {
|
||||||
|
ui.style_mut().interaction.selectable_labels = true;
|
||||||
|
ui.label(
|
||||||
|
"This tooltip is interactive, because the text in it is selectable.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||||
|
self.misc_tests(ui);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tooltips {
|
||||||
|
fn misc_tests(&mut self, ui: &mut egui::Ui) {
|
||||||
ui.label("All labels in this demo have tooltips.")
|
ui.label("All labels in this demo have tooltips.")
|
||||||
.on_hover_text("Yes, even this one.");
|
.on_hover_text("Yes, even this one.");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue