diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 33cabc000f47acd208414b0c348da3312597649d..2d2c13a9de3462540758f8a037ec2a28f3fed589 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -33,9 +33,9 @@ use util::ResultExt; use crate::{ current_platform, hash, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle, - Asset, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, - Entity, EventEmitter, FocusHandle, FocusId, ForegroundExecutor, Global, KeyBinding, Keymap, - Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, + Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem, Context, DispatchPhase, + DisplayId, Entity, EventEmitter, FocusHandle, FocusId, ForegroundExecutor, Global, KeyBinding, + Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, WindowAppearance, @@ -1612,6 +1612,12 @@ pub struct AnyTooltip { /// The absolute position of the mouse when the tooltip was deployed. pub mouse_position: Point, + + /// Whether the tooltitp can be hovered or not. + pub hoverable: bool, + + /// Bounds of the element that triggered the tooltip appearance. + pub origin_bounds: Bounds, } /// A keystroke event, and potentially the associated action diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 3f826ca00790bea20cce77fb54f8a6e2ee91e91f..19e7e7ab7d3be51879e87d81de15e19b89c819c7 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -1923,6 +1923,7 @@ impl Interactivity { cx.on_mouse_event({ let active_tooltip = active_tooltip.clone(); let hitbox = hitbox.clone(); + let source_bounds = hitbox.bounds; let tooltip_id = self.tooltip_id; move |_: &MouseMoveEvent, phase, cx| { let is_hovered = @@ -1952,6 +1953,8 @@ impl Interactivity { tooltip: Some(AnyTooltip { view: build_tooltip(cx), mouse_position: cx.mouse_position(), + hoverable: tooltip_is_hoverable, + origin_bounds: source_bounds, }), _task: None, }); diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index 427097d1b7acba2c88d1d473cc3f78e66c62c1e6..b35ec195ba948e93ecc4f8e00b5d9a58943a118c 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -675,6 +675,7 @@ impl Element for InteractiveText { if let Some(tooltip_builder) = self.tooltip_builder.clone() { let hitbox = hitbox.clone(); + let source_bounds = hitbox.bounds; let active_tooltip = interactive_state.active_tooltip.clone(); let pending_mouse_down = interactive_state.mouse_down_index.clone(); let text_layout = text_layout.clone(); @@ -708,6 +709,8 @@ impl Element for InteractiveText { tooltip: Some(AnyTooltip { view: tooltip, mouse_position: cx.mouse_position(), + hoverable: true, + origin_bounds: source_bounds, }), _task: None, } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 437de7dcb31c38d27c8533e057d7777cb7004896..a20ad90c1fcea84942778e8eed1b8668e5f332e9 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1552,6 +1552,19 @@ impl<'a> WindowContext<'a> { let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self); let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size); + // Element's parent can get hidden (e.g. via the `visible_on_hover` method), + // and element's `paint` won't be called (ergo, mouse listeners also won't be active) to detect that the tooltip has to be removed. + // Ensure it's not stuck around in such cases. + let invalidate_tooltip = !tooltip_request + .tooltip + .origin_bounds + .contains(&self.mouse_position()) + && (!tooltip_request.tooltip.hoverable + || !tooltip_bounds.contains(&self.mouse_position())); + if invalidate_tooltip { + return None; + } + let window_bounds = Bounds { origin: Point::default(), size: self.viewport_size(),