editor: Dismiss drag selection when dropped outside editor (#32382)

CharlesChen0823 and Smit Barmase created

This PR fixes two issues:

1. On macOS, using Alt to copy the selection instead of cutting it.
2. Dropping the drag selection outside the editor dismisses it.  


https://github.com/user-attachments/assets/341e21c3-3eca-4e58-9bcc-8ec1de18e999


Release Notes:

- N/A

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>

Change summary

crates/editor/src/editor.rs  |  1 +
crates/editor/src/element.rs | 38 ++++++++++++++++++++++++--------------
2 files changed, 25 insertions(+), 14 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -918,6 +918,7 @@ enum SelectionDragState {
     Dragging {
         selection: Selection<Anchor>,
         drop_cursor: Selection<Anchor>,
+        hide_drop_cursor: bool,
     },
 }
 

crates/editor/src/element.rs 🔗

@@ -856,8 +856,11 @@ impl EditorElement {
             SelectionDragState::Dragging { ref selection, .. } => {
                 let snapshot = editor.snapshot(window, cx);
                 let selection_display = selection.map(|anchor| anchor.to_display_point(&snapshot));
-                if !point_for_position.intersects_selection(&selection_display) {
-                    let is_cut = !event.modifiers.control;
+                if !point_for_position.intersects_selection(&selection_display)
+                    && text_hitbox.is_hovered(window)
+                {
+                    let is_cut = !(cfg!(target_os = "macos") && event.modifiers.alt
+                        || cfg!(not(target_os = "macos")) && event.modifiers.control);
                     editor.move_selection_on_drop(
                         &selection.clone(),
                         point_for_position.previous_valid,
@@ -865,10 +868,11 @@ impl EditorElement {
                         window,
                         cx,
                     );
-                    editor.selection_drag_state = SelectionDragState::None;
-                    cx.stop_propagation();
-                    return;
                 }
+                editor.selection_drag_state = SelectionDragState::None;
+                cx.stop_propagation();
+                cx.notify();
+                return;
             }
             _ => {}
         }
@@ -941,7 +945,8 @@ impl EditorElement {
             return;
         }
 
-        let text_bounds = position_map.text_hitbox.bounds;
+        let text_hitbox = &position_map.text_hitbox;
+        let text_bounds = text_hitbox.bounds;
         let point_for_position = position_map.point_for_position(event.position);
 
         let mut scroll_delta = gpui::Point::<f32>::default();
@@ -982,10 +987,12 @@ impl EditorElement {
             match editor.selection_drag_state {
                 SelectionDragState::Dragging {
                     ref mut drop_cursor,
+                    ref mut hide_drop_cursor,
                     ..
                 } => {
                     drop_cursor.start = drop_anchor;
                     drop_cursor.end = drop_anchor;
+                    *hide_drop_cursor = !text_hitbox.is_hovered(window);
                 }
                 SelectionDragState::ReadyToDrag { ref selection, .. } => {
                     let drop_cursor = Selection {
@@ -998,6 +1005,7 @@ impl EditorElement {
                     editor.selection_drag_state = SelectionDragState::Dragging {
                         selection: selection.clone(),
                         drop_cursor,
+                        hide_drop_cursor: false,
                     };
                 }
                 _ => {}
@@ -1251,16 +1259,18 @@ impl EditorElement {
                 if let SelectionDragState::Dragging {
                     ref selection,
                     ref drop_cursor,
+                    ref hide_drop_cursor,
                 } = editor.selection_drag_state
                 {
-                    if drop_cursor
-                        .start
-                        .cmp(&selection.start, &snapshot.buffer_snapshot)
-                        .eq(&Ordering::Less)
-                        || drop_cursor
-                            .end
-                            .cmp(&selection.end, &snapshot.buffer_snapshot)
-                            .eq(&Ordering::Greater)
+                    if !hide_drop_cursor
+                        && (drop_cursor
+                            .start
+                            .cmp(&selection.start, &snapshot.buffer_snapshot)
+                            .eq(&Ordering::Less)
+                            || drop_cursor
+                                .end
+                                .cmp(&selection.end, &snapshot.buffer_snapshot)
+                                .eq(&Ordering::Greater))
                     {
                         let drag_cursor_layout = SelectionLayout::new(
                             drop_cursor.clone(),