Delay hiding git blame tooltip (#22644)
It's easy to overshoot the bottom of the tooltip when cursoring to a button, such as opening the commit from a blame tooltip. Before this change the tooltip would immediately disappear, and now it sticks around for a bit. Also: * Shares the implementation with `elements/text.rs`. This will particularly be handy when it makes use of hoverable tooltips. * Improves the fix to #21657. - Now the element will no longer think it has an active tooltip that it registers with the window. - It will instead display the next available tooltip, whereas I believe before the next available tooltip would be suppressed. * Fixes bug where `cx.refresh()` wasn't called when text tooltip is hidden due to a mouse down event. * Ports over fix in https://github.com/zed-industries/zed/pull/14832 to `elements/text.rs` Release Notes: - The tooltip for inline git blame now waits a bit before disappearing when the mouse leaves it.
This commit is contained in:
parent
985544ffb9
commit
ac214c52c9
4 changed files with 391 additions and 215 deletions
|
@ -1550,62 +1550,71 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
|
||||
fn prepaint_tooltip(&mut self) -> Option<AnyElement> {
|
||||
let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
|
||||
let tooltip_request = tooltip_request.unwrap();
|
||||
let mut element = tooltip_request.tooltip.view.clone().into_any();
|
||||
let mouse_position = tooltip_request.tooltip.mouse_position;
|
||||
let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self);
|
||||
// Use indexing instead of iteration to avoid borrowing self for the duration of the loop.
|
||||
for tooltip_request_index in (0..self.window.next_frame.tooltip_requests.len()).rev() {
|
||||
let Some(Some(tooltip_request)) = self
|
||||
.window
|
||||
.next_frame
|
||||
.tooltip_requests
|
||||
.get(tooltip_request_index)
|
||||
.cloned()
|
||||
else {
|
||||
log::error!("Unexpectedly absent TooltipRequest");
|
||||
continue;
|
||||
};
|
||||
let mut element = tooltip_request.tooltip.view.clone().into_any();
|
||||
let mouse_position = tooltip_request.tooltip.mouse_position;
|
||||
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);
|
||||
let window_bounds = Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.viewport_size(),
|
||||
};
|
||||
let mut tooltip_bounds =
|
||||
Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
|
||||
let window_bounds = Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.viewport_size(),
|
||||
};
|
||||
|
||||
if tooltip_bounds.right() > window_bounds.right() {
|
||||
let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
|
||||
if new_x >= Pixels::ZERO {
|
||||
tooltip_bounds.origin.x = new_x;
|
||||
} else {
|
||||
tooltip_bounds.origin.x = cmp::max(
|
||||
Pixels::ZERO,
|
||||
tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
|
||||
);
|
||||
if tooltip_bounds.right() > window_bounds.right() {
|
||||
let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
|
||||
if new_x >= Pixels::ZERO {
|
||||
tooltip_bounds.origin.x = new_x;
|
||||
} else {
|
||||
tooltip_bounds.origin.x = cmp::max(
|
||||
Pixels::ZERO,
|
||||
tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tooltip_bounds.bottom() > window_bounds.bottom() {
|
||||
let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
|
||||
if new_y >= Pixels::ZERO {
|
||||
tooltip_bounds.origin.y = new_y;
|
||||
} else {
|
||||
tooltip_bounds.origin.y = cmp::max(
|
||||
Pixels::ZERO,
|
||||
tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
|
||||
);
|
||||
if tooltip_bounds.bottom() > window_bounds.bottom() {
|
||||
let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
|
||||
if new_y >= Pixels::ZERO {
|
||||
tooltip_bounds.origin.y = new_y;
|
||||
} else {
|
||||
tooltip_bounds.origin.y = cmp::max(
|
||||
Pixels::ZERO,
|
||||
tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible for an element to have an active tooltip while not being painted (e.g.
|
||||
// via the `visible_on_hover` method). Since mouse listeners are not active in this
|
||||
// case, instead update the tooltip's visibility here.
|
||||
let is_visible =
|
||||
(tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self);
|
||||
if !is_visible {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
|
||||
|
||||
self.window.tooltip_bounds = Some(TooltipBounds {
|
||||
id: tooltip_request.id,
|
||||
bounds: tooltip_bounds,
|
||||
});
|
||||
return Some(element);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
|
||||
|
||||
self.window.tooltip_bounds = Some(TooltipBounds {
|
||||
id: tooltip_request.id,
|
||||
bounds: tooltip_bounds,
|
||||
});
|
||||
Some(element)
|
||||
None
|
||||
}
|
||||
|
||||
fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue