Add mouse handling to gpui input example (#13960)

Conrad Irwin created

Release Notes:

- N/A

Change summary

crates/gpui/examples/input.rs | 38 +++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

Detailed changes

crates/gpui/examples/input.rs 🔗

@@ -26,6 +26,8 @@ struct TextInput {
     selection_reversed: bool,
     marked_range: Option<Range<usize>>,
     last_layout: Option<ShapedLine>,
+    last_bounds: Option<Bounds<Pixels>>,
+    is_selecting: bool,
 }
 
 impl TextInput {
@@ -80,6 +82,21 @@ impl TextInput {
         self.replace_text_in_range(None, "", cx)
     }
 
+    fn on_mouse_down(&mut self, event: &MouseDownEvent, cx: &mut ViewContext<Self>) {
+        self.is_selecting = true;
+        self.move_to(self.index_for_mouse_position(event.position), cx)
+    }
+
+    fn on_mouse_up(&mut self, _: &MouseUpEvent, _: &mut ViewContext<Self>) {
+        self.is_selecting = false;
+    }
+
+    fn on_mouse_move(&mut self, event: &MouseMoveEvent, cx: &mut ViewContext<Self>) {
+        if self.is_selecting {
+            self.select_to(self.index_for_mouse_position(event.position), cx);
+        }
+    }
+
     fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
         cx.show_character_palette();
     }
@@ -97,6 +114,20 @@ impl TextInput {
         }
     }
 
+    fn index_for_mouse_position(&self, position: Point<Pixels>) -> usize {
+        let (Some(bounds), Some(line)) = (self.last_bounds.as_ref(), self.last_layout.as_ref())
+        else {
+            return 0;
+        };
+        if position.y < bounds.top() {
+            return 0;
+        }
+        if position.y > bounds.bottom() {
+            return self.content.len();
+        }
+        line.closest_index_for_x(position.x - bounds.left())
+    }
+
     fn select_to(&mut self, offset: usize, cx: &mut ViewContext<Self>) {
         if self.selection_reversed {
             self.selected_range.start = offset
@@ -409,6 +440,7 @@ impl Element for TextElement {
         }
         self.input.update(cx, |input, _cx| {
             input.last_layout = Some(line);
+            input.last_bounds = Some(bounds);
         });
     }
 }
@@ -429,6 +461,10 @@ impl Render for TextInput {
             .on_action(cx.listener(Self::home))
             .on_action(cx.listener(Self::end))
             .on_action(cx.listener(Self::show_character_palette))
+            .on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down))
+            .on_mouse_up(MouseButton::Left, cx.listener(Self::on_mouse_up))
+            .on_mouse_up_out(MouseButton::Left, cx.listener(Self::on_mouse_up))
+            .on_mouse_move(cx.listener(Self::on_mouse_move))
             .bg(rgb(0xeeeeee))
             .size_full()
             .line_height(px(30.))
@@ -475,6 +511,8 @@ fn main() {
                         selection_reversed: false,
                         marked_range: None,
                         last_layout: None,
+                        last_bounds: None,
+                        is_selecting: false,
                     })
                 },
             )