Account for all hover heights & prevent un-hover between popovers (#9257)
Fixes https://github.com/zed-industries/zed/issues/5227 Fixes https://github.com/zed-industries/zed/issues/7304 Release Notes: - Fixed an issue where not all editor hover popovers would be accounted for when choosing to hover above or below the mouse cursor ([#5227](https://github.com/zed-industries/zed/issues/5227)). - Fixed an issue where editor hover could be dismissed by moving the mouse between hover popovers ([#7304](https://github.com/zed-industries/zed/issues/7304)). Co-authored-by: Antonio Scandurra <antonio@zed.dev>
This commit is contained in:
parent
139bb3275a
commit
88e33a1dbe
1 changed files with 52 additions and 24 deletions
|
@ -1650,6 +1650,12 @@ impl EditorElement {
|
|||
em_width: Pixels,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
struct MeasuredHoverPopover {
|
||||
element: AnyElement,
|
||||
size: Size<Pixels>,
|
||||
horizontal_offset: Pixels,
|
||||
}
|
||||
|
||||
let max_size = size(
|
||||
(120. * em_width) // Default size
|
||||
.min(hitbox.size.width / 2.) // Shrink to half of the editor width
|
||||
|
@ -1669,7 +1675,7 @@ impl EditorElement {
|
|||
cx,
|
||||
)
|
||||
});
|
||||
let Some((position, mut hover_popovers)) = hover_popovers else {
|
||||
let Some((position, hover_popovers)) = hover_popovers else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -1679,48 +1685,70 @@ impl EditorElement {
|
|||
let hovered_row_layout =
|
||||
&line_layouts[(position.row() - visible_display_row_range.start) as usize].line;
|
||||
|
||||
// Minimum required size: Take the first popover, and add 1.5 times the minimum popover
|
||||
// height. This is the size we will use to decide whether to render popovers above or below
|
||||
// the hovered line.
|
||||
let first_size = hover_popovers[0].measure(available_space, cx);
|
||||
let height_to_reserve = first_size.height + 1.5 * MIN_POPOVER_LINE_HEIGHT * line_height;
|
||||
|
||||
// Compute Hovered Point
|
||||
let x =
|
||||
hovered_row_layout.x_for_index(position.column() as usize) - scroll_pixel_position.x;
|
||||
let y = position.row() as f32 * line_height - scroll_pixel_position.y;
|
||||
let hovered_point = content_origin + point(x, y);
|
||||
|
||||
if hovered_point.y - height_to_reserve > Pixels::ZERO {
|
||||
// There is enough space above. Render popovers above the hovered point
|
||||
let mut current_y = hovered_point.y;
|
||||
let mut overall_height = Pixels::ZERO;
|
||||
let mut measured_hover_popovers = Vec::new();
|
||||
for mut hover_popover in hover_popovers {
|
||||
let size = hover_popover.measure(available_space, cx);
|
||||
let mut popover_origin = point(hovered_point.x, current_y - size.height);
|
||||
let horizontal_offset =
|
||||
(text_hitbox.upper_right().x - (hovered_point.x + size.width)).min(Pixels::ZERO);
|
||||
|
||||
let x_out_of_bounds = text_hitbox.upper_right().x - (popover_origin.x + size.width);
|
||||
if x_out_of_bounds < Pixels::ZERO {
|
||||
popover_origin.x = popover_origin.x + x_out_of_bounds;
|
||||
overall_height += HOVER_POPOVER_GAP + size.height;
|
||||
|
||||
measured_hover_popovers.push(MeasuredHoverPopover {
|
||||
element: hover_popover,
|
||||
size,
|
||||
horizontal_offset,
|
||||
});
|
||||
}
|
||||
overall_height += HOVER_POPOVER_GAP;
|
||||
|
||||
fn draw_occluder(width: Pixels, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
|
||||
let mut occlusion = div()
|
||||
.size_full()
|
||||
.occlude()
|
||||
.on_mouse_move(|_, cx| cx.stop_propagation())
|
||||
.into_any_element();
|
||||
occlusion.measure(size(width, HOVER_POPOVER_GAP).into(), cx);
|
||||
cx.defer_draw(occlusion, origin, 2);
|
||||
}
|
||||
|
||||
cx.defer_draw(hover_popover, popover_origin, 2);
|
||||
if hovered_point.y > overall_height {
|
||||
// There is enough space above. Render popovers above the hovered point
|
||||
let mut current_y = hovered_point.y;
|
||||
for (position, popover) in measured_hover_popovers.into_iter().with_position() {
|
||||
let size = popover.size;
|
||||
let popover_origin = point(
|
||||
hovered_point.x + popover.horizontal_offset,
|
||||
current_y - size.height,
|
||||
);
|
||||
|
||||
cx.defer_draw(popover.element, popover_origin, 2);
|
||||
if position != itertools::Position::Last {
|
||||
let origin = point(popover_origin.x, popover_origin.y - HOVER_POPOVER_GAP);
|
||||
draw_occluder(size.width, origin, cx);
|
||||
}
|
||||
|
||||
current_y = popover_origin.y - HOVER_POPOVER_GAP;
|
||||
}
|
||||
} else {
|
||||
// There is not enough space above. Render popovers below the hovered point
|
||||
let mut current_y = hovered_point.y + line_height;
|
||||
for mut hover_popover in hover_popovers {
|
||||
let size = hover_popover.measure(available_space, cx);
|
||||
let mut popover_origin = point(hovered_point.x, current_y);
|
||||
for (position, popover) in measured_hover_popovers.into_iter().with_position() {
|
||||
let size = popover.size;
|
||||
let popover_origin = point(hovered_point.x + popover.horizontal_offset, current_y);
|
||||
|
||||
let x_out_of_bounds = text_hitbox.upper_right().x - (popover_origin.x + size.width);
|
||||
if x_out_of_bounds < Pixels::ZERO {
|
||||
popover_origin.x = popover_origin.x + x_out_of_bounds;
|
||||
cx.defer_draw(popover.element, popover_origin, 2);
|
||||
if position != itertools::Position::Last {
|
||||
let origin = point(popover_origin.x, popover_origin.y + size.height);
|
||||
draw_occluder(size.width, origin, cx);
|
||||
}
|
||||
|
||||
cx.defer_draw(hover_popover, popover_origin, 2);
|
||||
|
||||
current_y = popover_origin.y + size.height + HOVER_POPOVER_GAP;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue