Automatically transfer focus on mouse down in `Interactivity::paint`

Antonio Scandurra created

Change summary

crates/gpui2/src/elements/div.rs | 20 ++++++++++++++++++++
crates/gpui2/src/window.rs       |  3 ++-
2 files changed, 22 insertions(+), 1 deletion(-)

Detailed changes

crates/gpui2/src/elements/div.rs 🔗

@@ -755,6 +755,26 @@ impl Interactivity {
             }
         }
 
+        // If this element can be focused, register a mouse down listener
+        // that will automatically transfer focus when hitting the element.
+        // This behavior can be suppressed by using `cx.prevent_default()`.
+        if let Some(focus_handle) = element_state.focus_handle.clone() {
+            cx.on_mouse_event({
+                let interactive_bounds = interactive_bounds.clone();
+                move |event: &MouseDownEvent, phase, cx| {
+                    if phase == DispatchPhase::Bubble
+                        && !cx.default_prevented()
+                        && interactive_bounds.visibly_contains(&event.position, cx)
+                    {
+                        cx.focus(&focus_handle);
+                        // If there is a parent that is also focusable, prevent it
+                        // from trasferring focus because we already did so.
+                        cx.prevent_default();
+                    }
+                }
+            });
+        }
+
         for listener in self.mouse_down_listeners.drain(..) {
             let interactive_bounds = interactive_bounds.clone();
             cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {

crates/gpui2/src/window.rs 🔗

@@ -1332,8 +1332,9 @@ impl<'a> WindowContext<'a> {
 
     /// Dispatch a mouse or keyboard event on the window.
     pub fn dispatch_event(&mut self, event: InputEvent) -> bool {
-        // Handlers may set this to false by calling `stop_propagation`
+        // Handlers may set this to false by calling `stop_propagation`.
         self.app.propagate_event = true;
+        // Handlers may set this to true by calling `prevent_default`.
         self.window.default_prevented = false;
 
         let event = match event {