Update tooltip code a bit

This fixes a tiny UX bug where the tooltip would appear to move if you
hovered over an element, then moved your mouse out and back within
500ms.

The fix is to retain the task, so we can drop it to cancel it when the
mouse leaves.

Also changes the time we construct the tooltip to the time it first
shows.
This commit is contained in:
Conrad Irwin 2023-11-03 21:40:28 -06:00
parent 4725cd2cd6
commit de5458cfe0
3 changed files with 32 additions and 35 deletions

View file

@ -899,6 +899,7 @@ pub(crate) struct AnyDrag {
pub cursor_offset: Point<Pixels>, pub cursor_offset: Point<Pixels>,
} }
#[derive(Clone)]
pub(crate) struct AnyTooltip { pub(crate) struct AnyTooltip {
pub view: AnyView, pub view: AnyView,
pub cursor_offset: Point<Pixels>, pub cursor_offset: Point<Pixels>,

View file

@ -2,7 +2,7 @@ use crate::{
div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, BorrowWindow, Bounds, div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, BorrowWindow, Bounds,
Component, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Component, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch,
Keystroke, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, Keystroke, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style,
StyleRefinement, View, ViewContext, StyleRefinement, Task, View, ViewContext,
}; };
use collections::HashMap; use collections::HashMap;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -627,50 +627,48 @@ pub trait ElementInteraction<V: 'static>: 'static {
let active_tooltip = element_state.active_tooltip.clone(); let active_tooltip = element_state.active_tooltip.clone();
let pending_mouse_down = element_state.pending_mouse_down.clone(); let pending_mouse_down = element_state.pending_mouse_down.clone();
cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble { if phase != DispatchPhase::Bubble {
return; return;
} }
let is_hovered = bounds.contains_point(&event.position) let is_hovered = bounds.contains_point(&event.position)
&& pending_mouse_down.lock().is_none(); && pending_mouse_down.lock().is_none();
let mut tooltip_lock = active_tooltip.lock(); if !is_hovered {
active_tooltip.lock().take();
if is_hovered { return;
if tooltip_lock.is_none() { }
*tooltip_lock = Some(ActiveTooltip {
view: tooltip_builder(view_state, cx),
visible: false,
coordinates: event.position,
});
if active_tooltip.lock().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone(); let active_tooltip = active_tooltip.clone();
cx.spawn(move |view, mut cx| async move { let tooltip_builder = tooltip_builder.clone();
cx.background_executor().timer(TOOLTIP_DELAY).await;
view.update(&mut cx, |_, cx| { move |view, mut cx| async move {
if let Some(active_tooltip) = active_tooltip.lock().as_mut() { cx.background_executor().timer(TOOLTIP_DELAY).await;
active_tooltip.visible = true; view.update(&mut cx, move |view_state, cx| {
active_tooltip.coordinates = active_tooltip.lock().replace(ActiveTooltip {
cx.mouse_position() + TOOLTIP_OFFSET; waiting: None,
} tooltip: Some(AnyTooltip {
view: tooltip_builder(view_state, cx),
cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
}),
});
cx.notify(); cx.notify();
}) })
.ok() .ok();
}) }
.detach(); });
} active_tooltip.lock().replace(ActiveTooltip {
} else { waiting: Some(task),
tooltip_lock.take(); tooltip: None,
});
} }
}); });
if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
if active_tooltip.visible { if active_tooltip.tooltip.is_some() {
cx.active_tooltip = Some(AnyTooltip { cx.active_tooltip = active_tooltip.tooltip.clone()
view: active_tooltip.view.clone(),
cursor_offset: active_tooltip.coordinates,
});
} }
} }
} }
@ -866,9 +864,9 @@ pub struct InteractiveElementState {
} }
struct ActiveTooltip { struct ActiveTooltip {
view: AnyView, #[allow(unused)] // used to drop the task
visible: bool, waiting: Option<Task<()>>,
coordinates: Point<Pixels>, tooltip: Option<AnyTooltip>,
} }
impl InteractiveElementState { impl InteractiveElementState {

View file

@ -1,5 +1,3 @@
use std::time::Duration;
use gpui2::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext}; use gpui2::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext};
use theme2::ActiveTheme; use theme2::ActiveTheme;