diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 371f554337..a102c71a6f 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -656,7 +656,6 @@ pub struct Interactivity { pub focusable: bool, pub tracked_focus_handle: Option, pub scroll_handle: Option, - pub focus_listeners: FocusListeners, pub group: Option, pub base_style: Box, pub focus_style: Option>, @@ -759,6 +758,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| { @@ -988,7 +1007,7 @@ impl Interactivity { } let active_state = element_state.clicked_state.clone(); - if !active_state.borrow().is_clicked() { + if active_state.borrow().is_clicked() { cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| { if phase == DispatchPhase::Capture { *active_state.borrow_mut() = ElementClickedState::default(); @@ -1002,7 +1021,7 @@ impl Interactivity { .and_then(|group_active| GroupBounds::get(&group_active.group, cx)); let interactive_bounds = interactive_bounds.clone(); cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble { + if phase == DispatchPhase::Bubble && !cx.default_prevented() { let group = active_group_bounds.map_or(false, |bounds| bounds.contains(&down.position)); let element = interactive_bounds.visibly_contains(&down.position, cx); @@ -1083,13 +1102,6 @@ impl Interactivity { cx.on_action(action_type, listener) } - if let Some(focus_handle) = element_state.focus_handle.as_ref() { - for listener in self.focus_listeners { - let focus_handle = focus_handle.clone(); - cx.on_focus_changed(move |event, cx| listener(&focus_handle, event, cx)); - } - } - f(style, scroll_offset.unwrap_or_default(), cx) }, ); @@ -1193,7 +1205,6 @@ impl Default for Interactivity { focusable: false, tracked_focus_handle: None, scroll_handle: None, - focus_listeners: Vec::default(), // scroll_offset: Point::default(), group: None, base_style: Box::new(StyleRefinement::default()), diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 7ec69f4d0e..77eb4e27be 100644 --- a/crates/gpui2/src/window.rs +++ b/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 { diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index 6dc8187690..a3a8845947 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/crates/storybook2/src/stories/focus.rs @@ -66,9 +66,11 @@ impl Render for FocusStory { let color_4 = theme.status().conflict; let color_5 = theme.status().ignored; let color_6 = theme.status().renamed; + let color_7 = theme.status().hint; div() .id("parent") + .active(|style| style.bg(color_7)) .track_focus(&self.parent_focus) .key_context("parent") .on_action(cx.listener(|_, _action: &ActionA, _cx| {