use std::ops::Range;use std::time::{Duration, Instant}; use collections::HashMap;use feature_flags::{DiffReviewFeatureFlag, FeatureFlagAppExt as _};use gpui::{ AnyElement, App, AvailableSpace, ClickEvent, Context, DefiniteLength, DispatchPhase, Element, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent, ParentElement, Pixels, PressureStage, ScrollDelta, ScrollWheelEvent, TextStyleRefinement, Window, anchored, deferred, point, px,};use multi_buffer::MultiBufferRow;use project::DisableAiSettings;use settings::Settings;use sum_tree::Bias;use text::SelectionGoal;use theme_settings::BufferLineHeight;use util::{RangeExt, debug_panic, post_inc}; use super::{EditorElement, EditorLayout, LineNumberLayout, PositionMap, SplitSide};use crate::{ CURSORS_VISIBLE_FOR, ColumnarMode, DisplayDiffHunk, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot, GutterHoverButton, HoveredCursor, JumpData, PhantomDiffReviewIndicator, SelectPhase, Selection, SelectionDragState, display_map::ToDisplayPoint, editor_settings::DoubleClickInMultibuffer, hover_popover::hover_at, mouse_context_menu, scroll::ScrollPixelOffset,}; impl EditorElement { pub(crate) fn mouse_moved( editor: &mut Editor, event: &MouseMoveEvent, position_map: &PositionMap, split_side: Option<SplitSide>, window: &mut Window, cx: &mut Context<Editor>, ) { let text_hitbox = &position_map.text_hitbox; let gutter_hitbox = &position_map.gutter_hitbox; let modifiers = event.modifiers; let text_hovered = text_hitbox.is_hovered(window); let gutter_hovered = gutter_hitbox.is_hovered(window); editor.set_gutter_hovered(gutter_hovered, cx); let point_for_position = position_map.point_for_position(event.position); let valid_point = point_for_position.nearest_valid; // Update diff review drag state if we're dragging if editor.diff_review_drag_state.is_some() { editor.update_diff_review_drag(valid_point.row(), window, cx); } 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(¤t_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 text_hovered && let Some((bounds, buffer_id, blame_entry)) = &position_map.inline_blame_bounds { let mouse_over_inline_blame = bounds.contains(&event.position); let mouse_over_popover = editor .inline_blame_popover .as_ref() .and_then(|state| state.popover_bounds) .is_some_and(|bounds| bounds.contains(&event.position)); let keyboard_grace = editor .inline_blame_popover .as_ref() .is_some_and(|state| state.keyboard_grace); if mouse_over_inline_blame || mouse_over_popover { editor.show_blame_popover(*buffer_id, blame_entry, event.position, false, cx); } else if !keyboard_grace { editor.hide_blame_popover(false, cx); } } else { let keyboard_grace = editor .inline_blame_popover .as_ref() .is_some_and(|state| state.keyboard_grace); if !keyboard_grace { editor.hide_blame_popover(false, cx); } } // Handle diff review indicator when gutter is hovered in diff mode with AI enabled let show_diff_review = editor.show_diff_review_button() && cx.has_flag::<DiffReviewFeatureFlag>() && !DisableAiSettings::is_ai_disabled_for_buffer( editor.buffer.read(cx).as_singleton().as_ref(), cx, ); let diff_review_indicator = if gutter_hovered && show_diff_review { let is_visible = editor .gutter_diff_review_indicator .0 .is_some_and(|indicator| indicator.is_active); if !is_visible { editor .gutter_diff_review_indicator .1 .get_or_insert_with(|| { cx.spawn(async move |this, cx| { cx.background_executor() .timer(Duration::from_millis(200)) .await; this.update(cx, |this, cx| { if let Some(indicator) = this.gutter_diff_review_indicator.0.as_mut() { indicator.is_active = true; cx.notify(); } }) .ok(); }) }); } let anchor = position_map .snapshot .display_point_to_anchor(valid_point, Bias::Left); Some(PhantomDiffReviewIndicator { start: anchor, end: anchor, is_active: is_visible, }) } else { editor.gutter_diff_review_indicator.1 = None; None }; if diff_review_indicator != editor.gutter_diff_review_indicator.0 { editor.gutter_diff_review_indicator.0 = diff_review_indicator; cx.notify(); } // Don't show breakpoint indicator when diff review indicator is active on this row let is_on_diff_review_button_row = diff_review_indicator.is_some_and(|indicator| { let start_row = indicator .start .to_display_point(&position_map.snapshot.display_snapshot) .row(); indicator.is_active && start_row == valid_point.row() }); let gutter_hover_button = if gutter_hovered && !is_on_diff_review_button_row && split_side != Some(SplitSide::Left) { let buffer_anchor = position_map .snapshot .display_point_to_anchor(valid_point, Bias::Left); if position_map .snapshot .buffer_snapshot() .anchor_to_buffer_anchor(buffer_anchor) .is_some() { let is_visible = editor .gutter_hover_button .0 .is_some_and(|indicator| indicator.is_active); if !is_visible { editor.gutter_hover_button.1.get_or_insert_with(|| { cx.spawn(async move |this, cx| { cx.background_executor() .timer(Duration::from_millis(200)) .await; this.update(cx, |this, cx| { if let Some(indicator) = this.gutter_hover_button.0.as_mut() { indicator.is_active = true; cx.notify(); } }) .ok(); }) }); } Some(GutterHoverButton { display_row: valid_point.row(), is_active: is_visible, }) } else { editor.gutter_hover_button.1 = None; None } } else if editor.has_mouse_context_menu() { editor.gutter_hover_button.1 = None; editor.gutter_hover_button.0 } else { editor.gutter_hover_button.1 = None; None }; if &gutter_hover_button != &editor.gutter_hover_button.0 { editor.gutter_hover_button.0 = gutter_hover_button; cx.notify(); } // Don't trigger hover popover if mouse is hovering over context menu if text_hovered { editor.update_hovered_link( point_for_position, Some(event.position), &position_map.snapshot, modifiers, window, cx, ); if let Some(point) = point_for_position.as_valid() { let anchor = position_map .snapshot .buffer_snapshot() .anchor_before(point.to_offset(&position_map.snapshot, Bias::Left)); hover_at(editor, Some(anchor), Some(event.position), window, cx); Self::update_visible_cursor(editor, point, position_map, window, cx); } else { editor.update_inlay_link_and_hover_points( &position_map.snapshot, point_for_position, Some(event.position), modifiers.secondary(), modifiers.shift, window, cx, ); } } else { editor.hide_hovered_link(cx); hover_at(editor, None, Some(event.position), window, cx); } } pub(super) fn layout_mouse_context_menu( &self, editor_snapshot: &EditorSnapshot, visible_range: Range<DisplayRow>, content_origin: gpui::Point<Pixels>, window: &mut Window, cx: &mut App, ) -> Option<AnyElement> { let position = self.editor.update(cx, |editor, cx| { let visible_start_point = editor.display_to_pixel_point( DisplayPoint::new(visible_range.start, 0), editor_snapshot, window, cx, )?; let visible_end_point = editor.display_to_pixel_point( DisplayPoint::new(visible_range.end, 0), editor_snapshot, window, cx, )?; let mouse_context_menu = editor.mouse_context_menu.as_ref()?; let (source_display_point, position) = match mouse_context_menu.position { mouse_context_menu::MenuPosition::PinnedToScreen(point) => (None, point), mouse_context_menu::MenuPosition::PinnedToEditor { source, offset } => { let source_display_point = source.to_display_point(editor_snapshot); let source_point = editor.to_pixel_point(source, editor_snapshot, window, cx)?; let position = content_origin + source_point + offset; (Some(source_display_point), position) } }; let source_included = source_display_point.is_none_or(|source_display_point| { visible_range .to_inclusive() .contains(&source_display_point.row()) }); let position_included = visible_start_point.y <= position.y && position.y <= visible_end_point.y; if !source_included && !position_included { None } else { Some(position) } })?; let text_style = TextStyleRefinement { line_height: Some(DefiniteLength::Fraction( BufferLineHeight::Comfortable.value(), )), ..Default::default() }; window.with_text_style(Some(text_style), |window| { let mut element = self.editor.read_with(cx, |editor, _| { let mouse_context_menu = editor.mouse_context_menu.as_ref()?; let context_menu = mouse_context_menu.context_menu.clone(); Some( deferred( anchored() .position(position) .child(context_menu) .anchor(gpui::Anchor::TopLeft) .snap_to_window_with_margin(px(8.)), ) .with_priority(1) .into_any(), ) })?; element.prepaint_as_root(position, AvailableSpace::min_size(), window, cx); Some(element) }) } pub(super) fn paint_mouse_listeners( &mut self, layout: &EditorLayout, window: &mut Window, cx: &mut App, ) { if layout.mode.is_minimap() { return; } self.paint_scroll_wheel_listener(layout, window, cx); window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); let line_numbers = layout.line_numbers.clone(); move |event: &MouseDownEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { match event.button { MouseButton::Left => editor.update(cx, |editor, cx| { let pending_mouse_down = editor .pending_mouse_down .get_or_insert_with(Default::default) .clone(); *pending_mouse_down.borrow_mut() = Some(event.clone()); Self::mouse_left_down( editor, event, &position_map, line_numbers.as_ref(), window, cx, ); }), MouseButton::Right => editor.update(cx, |editor, cx| { Self::mouse_right_down(editor, event, &position_map, window, cx); }), MouseButton::Middle => editor.update(cx, |editor, cx| { Self::mouse_middle_down(editor, event, &position_map, window, cx); }), _ => {} }; } } }); window.on_mouse_event({ let editor = self.editor.clone(); let position_map = layout.position_map.clone(); move |event: &MouseUpEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { Self::mouse_up(editor, event, &position_map, window, cx) }); } } }); window.on_mouse_event({ let editor = self.editor.clone(); let position_map = layout.position_map.clone(); let mut captured_mouse_down = None; move |event: &MouseUpEvent, phase, window, cx| match phase { // Clear the pending mouse down during the capture phase, // so that it happens even if another event handler stops // propagation. DispatchPhase::Capture => editor.update(cx, |editor, _cx| { let pending_mouse_down = editor .pending_mouse_down .get_or_insert_with(Default::default) .clone(); let mut pending_mouse_down = pending_mouse_down.borrow_mut(); if pending_mouse_down.is_some() && position_map.text_hitbox.is_hovered(window) { captured_mouse_down = pending_mouse_down.take(); window.refresh(); } }), // Fire click handlers during the bubble phase. DispatchPhase::Bubble => editor.update(cx, |editor, cx| { if let Some(mouse_down) = captured_mouse_down.take() { let event = ClickEvent::Mouse(MouseClickEvent { down: mouse_down, up: event.clone(), }); Self::click(editor, &event, &position_map, window, cx); } }), } }); window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); move |event: &MousePressureEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { Self::pressure_click(editor, &event, &position_map, window, cx); }) } } }); window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); let split_side = self.split_side; move |event: &MouseMoveEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { if editor.hover_state.focused(window, cx) { return; } if event.pressed_button == Some(MouseButton::Left) || event.pressed_button == Some(MouseButton::Middle) { Self::mouse_dragged(editor, event, &position_map, window, cx) } Self::mouse_moved(editor, event, &position_map, split_side, window, cx) }); } } }); } fn paint_scroll_wheel_listener( &mut self, layout: &EditorLayout, window: &mut Window, cx: &mut App, ) { window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); let hitbox = layout.hitbox.clone(); let mut delta = ScrollDelta::default(); // Set a minimum scroll_sensitivity of 0.01 to make sure the user doesn't // accidentally turn off their scrolling. let base_scroll_sensitivity = EditorSettings::get_global(cx).scroll_sensitivity.max(0.01); // Use a minimum fast_scroll_sensitivity for same reason above let fast_scroll_sensitivity = EditorSettings::get_global(cx)Lines 1–500 of 1191. Load more
use std::ops::Range; use std::time::{Duration, Instant}; use collections::HashMap; use feature_flags::{DiffReviewFeatureFlag, FeatureFlagAppExt as _}; use gpui::{ AnyElement, App, AvailableSpace, ClickEvent, Context, DefiniteLength, DispatchPhase, Element, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent, ParentElement, Pixels, PressureStage, ScrollDelta, ScrollWheelEvent, TextStyleRefinement, Window, anchored, deferred, point, px, }; use multi_buffer::MultiBufferRow; use project::DisableAiSettings; use settings::Settings; use sum_tree::Bias; use text::SelectionGoal; use theme_settings::BufferLineHeight; use util::{RangeExt, debug_panic, post_inc}; use super::{EditorElement, EditorLayout, LineNumberLayout, PositionMap, SplitSide}; use crate::{ CURSORS_VISIBLE_FOR, ColumnarMode, DisplayDiffHunk, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot, GutterHoverButton, HoveredCursor, JumpData, PhantomDiffReviewIndicator, SelectPhase, Selection, SelectionDragState, display_map::ToDisplayPoint, editor_settings::DoubleClickInMultibuffer, hover_popover::hover_at, mouse_context_menu, scroll::ScrollPixelOffset, }; impl EditorElement { pub(crate) fn mouse_moved( editor: &mut Editor, event: &MouseMoveEvent, position_map: &PositionMap, split_side: Option<SplitSide>, window: &mut Window, cx: &mut Context<Editor>, ) { let text_hitbox = &position_map.text_hitbox; let gutter_hitbox = &position_map.gutter_hitbox; let modifiers = event.modifiers; let text_hovered = text_hitbox.is_hovered(window); let gutter_hovered = gutter_hitbox.is_hovered(window); editor.set_gutter_hovered(gutter_hovered, cx); let point_for_position = position_map.point_for_position(event.position); let valid_point = point_for_position.nearest_valid; // Update diff review drag state if we're dragging if editor.diff_review_drag_state.is_some() { editor.update_diff_review_drag(valid_point.row(), window, cx); } 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(¤t_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 text_hovered && let Some((bounds, buffer_id, blame_entry)) = &position_map.inline_blame_bounds { let mouse_over_inline_blame = bounds.contains(&event.position); let mouse_over_popover = editor .inline_blame_popover .as_ref() .and_then(|state| state.popover_bounds) .is_some_and(|bounds| bounds.contains(&event.position)); let keyboard_grace = editor .inline_blame_popover .as_ref() .is_some_and(|state| state.keyboard_grace); if mouse_over_inline_blame || mouse_over_popover { editor.show_blame_popover(*buffer_id, blame_entry, event.position, false, cx); } else if !keyboard_grace { editor.hide_blame_popover(false, cx); } } else { let keyboard_grace = editor .inline_blame_popover .as_ref() .is_some_and(|state| state.keyboard_grace); if !keyboard_grace { editor.hide_blame_popover(false, cx); } } // Handle diff review indicator when gutter is hovered in diff mode with AI enabled let show_diff_review = editor.show_diff_review_button() && cx.has_flag::<DiffReviewFeatureFlag>() && !DisableAiSettings::is_ai_disabled_for_buffer( editor.buffer.read(cx).as_singleton().as_ref(), cx, ); let diff_review_indicator = if gutter_hovered && show_diff_review { let is_visible = editor .gutter_diff_review_indicator .0 .is_some_and(|indicator| indicator.is_active); if !is_visible { editor .gutter_diff_review_indicator .1 .get_or_insert_with(|| { cx.spawn(async move |this, cx| { cx.background_executor() .timer(Duration::from_millis(200)) .await; this.update(cx, |this, cx| { if let Some(indicator) = this.gutter_diff_review_indicator.0.as_mut() { indicator.is_active = true; cx.notify(); } }) .ok(); }) }); } let anchor = position_map .snapshot .display_point_to_anchor(valid_point, Bias::Left); Some(PhantomDiffReviewIndicator { start: anchor, end: anchor, is_active: is_visible, }) } else { editor.gutter_diff_review_indicator.1 = None; None }; if diff_review_indicator != editor.gutter_diff_review_indicator.0 { editor.gutter_diff_review_indicator.0 = diff_review_indicator; cx.notify(); } // Don't show breakpoint indicator when diff review indicator is active on this row let is_on_diff_review_button_row = diff_review_indicator.is_some_and(|indicator| { let start_row = indicator .start .to_display_point(&position_map.snapshot.display_snapshot) .row(); indicator.is_active && start_row == valid_point.row() }); let gutter_hover_button = if gutter_hovered && !is_on_diff_review_button_row && split_side != Some(SplitSide::Left) { let buffer_anchor = position_map .snapshot .display_point_to_anchor(valid_point, Bias::Left); if position_map .snapshot .buffer_snapshot() .anchor_to_buffer_anchor(buffer_anchor) .is_some() { let is_visible = editor .gutter_hover_button .0 .is_some_and(|indicator| indicator.is_active); if !is_visible { editor.gutter_hover_button.1.get_or_insert_with(|| { cx.spawn(async move |this, cx| { cx.background_executor() .timer(Duration::from_millis(200)) .await; this.update(cx, |this, cx| { if let Some(indicator) = this.gutter_hover_button.0.as_mut() { indicator.is_active = true; cx.notify(); } }) .ok(); }) }); } Some(GutterHoverButton { display_row: valid_point.row(), is_active: is_visible, }) } else { editor.gutter_hover_button.1 = None; None } } else if editor.has_mouse_context_menu() { editor.gutter_hover_button.1 = None; editor.gutter_hover_button.0 } else { editor.gutter_hover_button.1 = None; None }; if &gutter_hover_button != &editor.gutter_hover_button.0 { editor.gutter_hover_button.0 = gutter_hover_button; cx.notify(); } // Don't trigger hover popover if mouse is hovering over context menu if text_hovered { editor.update_hovered_link( point_for_position, Some(event.position), &position_map.snapshot, modifiers, window, cx, ); if let Some(point) = point_for_position.as_valid() { let anchor = position_map .snapshot .buffer_snapshot() .anchor_before(point.to_offset(&position_map.snapshot, Bias::Left)); hover_at(editor, Some(anchor), Some(event.position), window, cx); Self::update_visible_cursor(editor, point, position_map, window, cx); } else { editor.update_inlay_link_and_hover_points( &position_map.snapshot, point_for_position, Some(event.position), modifiers.secondary(), modifiers.shift, window, cx, ); } } else { editor.hide_hovered_link(cx); hover_at(editor, None, Some(event.position), window, cx); } } pub(super) fn layout_mouse_context_menu( &self, editor_snapshot: &EditorSnapshot, visible_range: Range<DisplayRow>, content_origin: gpui::Point<Pixels>, window: &mut Window, cx: &mut App, ) -> Option<AnyElement> { let position = self.editor.update(cx, |editor, cx| { let visible_start_point = editor.display_to_pixel_point( DisplayPoint::new(visible_range.start, 0), editor_snapshot, window, cx, )?; let visible_end_point = editor.display_to_pixel_point( DisplayPoint::new(visible_range.end, 0), editor_snapshot, window, cx, )?; let mouse_context_menu = editor.mouse_context_menu.as_ref()?; let (source_display_point, position) = match mouse_context_menu.position { mouse_context_menu::MenuPosition::PinnedToScreen(point) => (None, point), mouse_context_menu::MenuPosition::PinnedToEditor { source, offset } => { let source_display_point = source.to_display_point(editor_snapshot); let source_point = editor.to_pixel_point(source, editor_snapshot, window, cx)?; let position = content_origin + source_point + offset; (Some(source_display_point), position) } }; let source_included = source_display_point.is_none_or(|source_display_point| { visible_range .to_inclusive() .contains(&source_display_point.row()) }); let position_included = visible_start_point.y <= position.y && position.y <= visible_end_point.y; if !source_included && !position_included { None } else { Some(position) } })?; let text_style = TextStyleRefinement { line_height: Some(DefiniteLength::Fraction( BufferLineHeight::Comfortable.value(), )), ..Default::default() }; window.with_text_style(Some(text_style), |window| { let mut element = self.editor.read_with(cx, |editor, _| { let mouse_context_menu = editor.mouse_context_menu.as_ref()?; let context_menu = mouse_context_menu.context_menu.clone(); Some( deferred( anchored() .position(position) .child(context_menu) .anchor(gpui::Anchor::TopLeft) .snap_to_window_with_margin(px(8.)), ) .with_priority(1) .into_any(), ) })?; element.prepaint_as_root(position, AvailableSpace::min_size(), window, cx); Some(element) }) } pub(super) fn paint_mouse_listeners( &mut self, layout: &EditorLayout, window: &mut Window, cx: &mut App, ) { if layout.mode.is_minimap() { return; } self.paint_scroll_wheel_listener(layout, window, cx); window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); let line_numbers = layout.line_numbers.clone(); move |event: &MouseDownEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { match event.button { MouseButton::Left => editor.update(cx, |editor, cx| { let pending_mouse_down = editor .pending_mouse_down .get_or_insert_with(Default::default) .clone(); *pending_mouse_down.borrow_mut() = Some(event.clone()); Self::mouse_left_down( editor, event, &position_map, line_numbers.as_ref(), window, cx, ); }), MouseButton::Right => editor.update(cx, |editor, cx| { Self::mouse_right_down(editor, event, &position_map, window, cx); }), MouseButton::Middle => editor.update(cx, |editor, cx| { Self::mouse_middle_down(editor, event, &position_map, window, cx); }), _ => {} }; } } }); window.on_mouse_event({ let editor = self.editor.clone(); let position_map = layout.position_map.clone(); move |event: &MouseUpEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { Self::mouse_up(editor, event, &position_map, window, cx) }); } } }); window.on_mouse_event({ let editor = self.editor.clone(); let position_map = layout.position_map.clone(); let mut captured_mouse_down = None; move |event: &MouseUpEvent, phase, window, cx| match phase { // Clear the pending mouse down during the capture phase, // so that it happens even if another event handler stops // propagation. DispatchPhase::Capture => editor.update(cx, |editor, _cx| { let pending_mouse_down = editor .pending_mouse_down .get_or_insert_with(Default::default) .clone(); let mut pending_mouse_down = pending_mouse_down.borrow_mut(); if pending_mouse_down.is_some() && position_map.text_hitbox.is_hovered(window) { captured_mouse_down = pending_mouse_down.take(); window.refresh(); } }), // Fire click handlers during the bubble phase. DispatchPhase::Bubble => editor.update(cx, |editor, cx| { if let Some(mouse_down) = captured_mouse_down.take() { let event = ClickEvent::Mouse(MouseClickEvent { down: mouse_down, up: event.clone(), }); Self::click(editor, &event, &position_map, window, cx); } }), } }); window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); move |event: &MousePressureEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { Self::pressure_click(editor, &event, &position_map, window, cx); }) } } }); window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); let split_side = self.split_side; move |event: &MouseMoveEvent, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { if editor.hover_state.focused(window, cx) { return; } if event.pressed_button == Some(MouseButton::Left) || event.pressed_button == Some(MouseButton::Middle) { Self::mouse_dragged(editor, event, &position_map, window, cx) } Self::mouse_moved(editor, event, &position_map, split_side, window, cx) }); } } }); } fn paint_scroll_wheel_listener( &mut self, layout: &EditorLayout, window: &mut Window, cx: &mut App, ) { window.on_mouse_event({ let position_map = layout.position_map.clone(); let editor = self.editor.clone(); let hitbox = layout.hitbox.clone(); let mut delta = ScrollDelta::default(); // Set a minimum scroll_sensitivity of 0.01 to make sure the user doesn't // accidentally turn off their scrolling. let base_scroll_sensitivity = EditorSettings::get_global(cx).scroll_sensitivity.max(0.01); // Use a minimum fast_scroll_sensitivity for same reason above let fast_scroll_sensitivity = EditorSettings::get_global(cx)