editor: Fix diff hunk controls not shown until buffer interaction (#32692)

Similar to https://github.com/zed-industries/zed/pull/32683, checking
mouse hovered in `mouse_move` instead of `prepaint` for diff hunk
controls.

Release Notes:

- Fixed issue where diff hunk controls were not visible on mouse hover
when `cursor_blink` is `false`.
This commit is contained in:
Smit Barmase 2025-06-13 22:29:49 +05:30 committed by GitHub
parent 71dbe88459
commit 2aa79a022e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 82 additions and 47 deletions

View file

@ -1088,6 +1088,7 @@ pub struct Editor {
tasks_update_task: Option<Task<()>>, tasks_update_task: Option<Task<()>>,
breakpoint_store: Option<Entity<BreakpointStore>>, breakpoint_store: Option<Entity<BreakpointStore>>,
gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>), gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
hovered_diff_hunk_row: Option<DisplayRow>,
pull_diagnostics_task: Task<()>, pull_diagnostics_task: Task<()>,
in_project_search: bool, in_project_search: bool,
previous_search_ranges: Option<Arc<[Range<Anchor>]>>, previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
@ -2016,6 +2017,7 @@ impl Editor {
breakpoint_store, breakpoint_store,
gutter_breakpoint_indicator: (None, None), gutter_breakpoint_indicator: (None, None),
hovered_diff_hunk_row: None,
_subscriptions: vec![ _subscriptions: vec![
cx.observe(&buffer, Self::on_buffer_changed), cx.observe(&buffer, Self::on_buffer_changed),
cx.subscribe_in(&buffer, window, Self::on_buffer_event), cx.subscribe_in(&buffer, window, Self::on_buffer_event),

View file

@ -1072,18 +1072,56 @@ impl EditorElement {
editor: &mut Editor, editor: &mut Editor,
event: &MouseMoveEvent, event: &MouseMoveEvent,
position_map: &PositionMap, position_map: &PositionMap,
inline_blame_bounds: &Option<(Bounds<Pixels>, BlameEntry)>,
window: &mut Window, window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) { ) {
let text_hitbox = &position_map.text_hitbox; let text_hitbox = &position_map.text_hitbox;
let gutter_hitbox = &position_map.gutter_hitbox; let gutter_hitbox = &position_map.gutter_hitbox;
let modifiers = event.modifiers; let modifiers = event.modifiers;
let text_hovered = text_hitbox.is_hovered(window);
let gutter_hovered = gutter_hitbox.is_hovered(window); let gutter_hovered = gutter_hitbox.is_hovered(window);
editor.set_gutter_hovered(gutter_hovered, cx); editor.set_gutter_hovered(gutter_hovered, cx);
editor.mouse_cursor_hidden = false; editor.mouse_cursor_hidden = false;
if let Some((bounds, blame_entry)) = inline_blame_bounds { let point_for_position = position_map.point_for_position(event.position);
let valid_point = point_for_position.previous_valid;
let hovered_diff_control = position_map
.diff_hunk_control_bounds
.iter()
.find(|(_, bounds)| bounds.contains(&event.position))
.map(|(row, _)| *row);
let hovered_diff_hunk_row = if let Some(control_row) = hovered_diff_control {
Some(control_row)
} else {
if text_hovered {
let current_row = valid_point.row();
position_map.display_hunks.iter().find_map(|(hunk, _)| {
if let DisplayDiffHunk::Unfolded {
display_row_range, ..
} = hunk
{
if display_row_range.contains(&current_row) {
Some(display_row_range.start)
} else {
None
}
} else {
None
}
})
} else {
None
}
};
if hovered_diff_hunk_row != editor.hovered_diff_hunk_row {
editor.hovered_diff_hunk_row = hovered_diff_hunk_row;
cx.notify();
}
if let Some((bounds, blame_entry)) = &position_map.inline_blame_bounds {
let mouse_over_inline_blame = bounds.contains(&event.position); let mouse_over_inline_blame = bounds.contains(&event.position);
let mouse_over_popover = editor let mouse_over_popover = editor
.inline_blame_popover .inline_blame_popover
@ -1101,12 +1139,9 @@ impl EditorElement {
} }
let breakpoint_indicator = if gutter_hovered { let breakpoint_indicator = if gutter_hovered {
let new_point = position_map
.point_for_position(event.position)
.previous_valid;
let buffer_anchor = position_map let buffer_anchor = position_map
.snapshot .snapshot
.display_point_to_anchor(new_point, Bias::Left); .display_point_to_anchor(valid_point, Bias::Left);
if let Some((buffer_snapshot, file)) = position_map if let Some((buffer_snapshot, file)) = position_map
.snapshot .snapshot
@ -1161,7 +1196,7 @@ impl EditorElement {
} }
Some(PhantomBreakpointIndicator { Some(PhantomBreakpointIndicator {
display_row: new_point.row(), display_row: valid_point.row(),
is_active: is_visible, is_active: is_visible,
collides_with_existing_breakpoint: has_existing_breakpoint, collides_with_existing_breakpoint: has_existing_breakpoint,
}) })
@ -1180,9 +1215,7 @@ impl EditorElement {
} }
// Don't trigger hover popover if mouse is hovering over context menu // Don't trigger hover popover if mouse is hovering over context menu
if text_hitbox.is_hovered(window) { if text_hovered {
let point_for_position = position_map.point_for_position(event.position);
editor.update_hovered_link( editor.update_hovered_link(
point_for_position, point_for_position,
&position_map.snapshot, &position_map.snapshot,
@ -4769,7 +4802,6 @@ impl EditorElement {
row_range: Range<DisplayRow>, row_range: Range<DisplayRow>,
row_infos: &[RowInfo], row_infos: &[RowInfo],
text_hitbox: &Hitbox, text_hitbox: &Hitbox,
position_map: &PositionMap,
newest_cursor_position: Option<DisplayPoint>, newest_cursor_position: Option<DisplayPoint>,
line_height: Pixels, line_height: Pixels,
right_margin: Pixels, right_margin: Pixels,
@ -4779,14 +4811,15 @@ impl EditorElement {
editor: Entity<Editor>, editor: Entity<Editor>,
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Vec<AnyElement> { ) -> (Vec<AnyElement>, Vec<(DisplayRow, Bounds<Pixels>)>) {
let render_diff_hunk_controls = editor.read(cx).render_diff_hunk_controls.clone(); let render_diff_hunk_controls = editor.read(cx).render_diff_hunk_controls.clone();
let point_for_position = position_map.point_for_position(window.mouse_position()); let hovered_diff_hunk_row = editor.read(cx).hovered_diff_hunk_row;
let mut controls = vec![]; let mut controls = vec![];
let mut control_bounds = vec![];
let active_positions = [ let active_positions = [
Some(point_for_position.previous_valid), hovered_diff_hunk_row.map(|row| DisplayPoint::new(row, 0)),
newest_cursor_position, newest_cursor_position,
]; ];
@ -4831,6 +4864,7 @@ impl EditorElement {
{ {
continue; continue;
} }
if active_positions if active_positions
.iter() .iter()
.any(|p| p.map_or(false, |p| display_row_range.contains(&p.row()))) .any(|p| p.map_or(false, |p| display_row_range.contains(&p.row())))
@ -4854,6 +4888,9 @@ impl EditorElement {
let x = text_hitbox.bounds.right() - right_margin - px(10.) - size.width; let x = text_hitbox.bounds.right() - right_margin - px(10.) - size.width;
let bounds = Bounds::new(gpui::Point::new(x, y), size);
control_bounds.push((display_row_range.start, bounds));
window.with_absolute_element_offset(gpui::Point::new(x, y), |window| { window.with_absolute_element_offset(gpui::Point::new(x, y), |window| {
element.prepaint(window, cx) element.prepaint(window, cx)
}); });
@ -4862,7 +4899,7 @@ impl EditorElement {
} }
} }
controls (controls, control_bounds)
} }
fn layout_signature_help( fn layout_signature_help(
@ -6699,10 +6736,6 @@ impl EditorElement {
window.on_mouse_event({ window.on_mouse_event({
let position_map = layout.position_map.clone(); let position_map = layout.position_map.clone();
let editor = self.editor.clone(); let editor = self.editor.clone();
let inline_blame_bounds = layout
.inline_blame_layout
.as_ref()
.map(|layout| (layout.bounds, layout.entry.clone()));
move |event: &MouseMoveEvent, phase, window, cx| { move |event: &MouseMoveEvent, phase, window, cx| {
if phase == DispatchPhase::Bubble { if phase == DispatchPhase::Bubble {
@ -6716,14 +6749,7 @@ impl EditorElement {
Self::mouse_dragged(editor, event, &position_map, window, cx) Self::mouse_dragged(editor, event, &position_map, window, cx)
} }
Self::mouse_moved( Self::mouse_moved(editor, event, &position_map, window, cx)
editor,
event,
&position_map,
&inline_blame_bounds,
window,
cx,
)
}); });
} }
} }
@ -8698,6 +8724,25 @@ impl Element for EditorElement {
let mode = snapshot.mode.clone(); let mode = snapshot.mode.clone();
let (diff_hunk_controls, diff_hunk_control_bounds) = if is_read_only {
(vec![], vec![])
} else {
self.layout_diff_hunk_controls(
start_row..end_row,
&row_infos,
&text_hitbox,
newest_selection_head,
line_height,
right_margin,
scroll_pixel_position,
&display_hunks,
&highlighted_rows,
self.editor.clone(),
window,
cx,
)
};
let position_map = Rc::new(PositionMap { let position_map = Rc::new(PositionMap {
size: bounds.size, size: bounds.size,
visible_row_range, visible_row_range,
@ -8710,32 +8755,17 @@ impl Element for EditorElement {
snapshot, snapshot,
gutter_hitbox: gutter_hitbox.clone(), gutter_hitbox: gutter_hitbox.clone(),
text_hitbox: text_hitbox.clone(), text_hitbox: text_hitbox.clone(),
inline_blame_bounds: inline_blame_layout
.as_ref()
.map(|layout| (layout.bounds, layout.entry.clone())),
display_hunks: display_hunks.clone(),
diff_hunk_control_bounds: diff_hunk_control_bounds.clone(),
}); });
self.editor.update(cx, |editor, _| { self.editor.update(cx, |editor, _| {
editor.last_position_map = Some(position_map.clone()) editor.last_position_map = Some(position_map.clone())
}); });
let diff_hunk_controls = if is_read_only {
vec![]
} else {
self.layout_diff_hunk_controls(
start_row..end_row,
&row_infos,
&text_hitbox,
&position_map,
newest_selection_head,
line_height,
right_margin,
scroll_pixel_position,
&display_hunks,
&highlighted_rows,
self.editor.clone(),
window,
cx,
)
};
EditorLayout { EditorLayout {
mode, mode,
position_map, position_map,
@ -9398,6 +9428,9 @@ pub(crate) struct PositionMap {
pub snapshot: EditorSnapshot, pub snapshot: EditorSnapshot,
pub text_hitbox: Hitbox, pub text_hitbox: Hitbox,
pub gutter_hitbox: Hitbox, pub gutter_hitbox: Hitbox,
pub inline_blame_bounds: Option<(Bounds<Pixels>, BlameEntry)>,
pub display_hunks: Vec<(DisplayDiffHunk, Option<Hitbox>)>,
pub diff_hunk_control_bounds: Vec<(DisplayRow, Bounds<Pixels>)>,
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]