Merge branch 'gpui2' of github.com:zed-industries/zed into gpui2

Marshall Bowers created

Change summary

crates/gpui3/src/elements/div.rs        |  5 +
crates/gpui3/src/focus.rs               | 21 +++++--
crates/gpui3/src/window.rs              | 17 ++++-
crates/storybook2/src/stories.rs        |  2 
crates/storybook2/src/stories/focus.rs  | 80 +++++++++++++++++++++++++++
crates/storybook2/src/story_selector.rs |  2 
6 files changed, 117 insertions(+), 10 deletions(-)

Detailed changes

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

@@ -324,7 +324,10 @@ where
             let focus_handle = focus_handle.clone();
             cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    cx.focus(&focus_handle);
+                    if !cx.default_prevented() {
+                        cx.focus(&focus_handle);
+                        cx.prevent_default();
+                    }
                 }
             })
         }

crates/gpui3/src/focus.rs 🔗

@@ -89,11 +89,16 @@ pub trait Focus: Interactive {
         self.listeners()
             .focus
             .push(Box::new(move |view, event, cx| {
-                if event
+                let descendant_blurred = event
+                    .blurred
+                    .as_ref()
+                    .map_or(false, |blurred| handle.contains(blurred, cx));
+                let descendant_focused = event
                     .focused
                     .as_ref()
-                    .map_or(false, |focused| focused.contains(&handle, cx))
-                {
+                    .map_or(false, |focused| handle.contains(focused, cx));
+
+                if !descendant_blurred && descendant_focused {
                     listener(view, event, cx)
                 }
             }));
@@ -114,11 +119,15 @@ pub trait Focus: Interactive {
         self.listeners()
             .focus
             .push(Box::new(move |view, event, cx| {
-                if event
+                let descendant_blurred = event
                     .blurred
                     .as_ref()
-                    .map_or(false, |blurred| handle.contains(&blurred, cx))
-                {
+                    .map_or(false, |blurred| handle.contains(blurred, cx));
+                let descendant_focused = event
+                    .focused
+                    .as_ref()
+                    .map_or(false, |focused| handle.contains(focused, cx));
+                if descendant_blurred && !descendant_focused {
                     listener(view, event, cx)
                 }
             }));

crates/gpui3/src/window.rs 🔗

@@ -160,6 +160,7 @@ pub struct Window {
     pub(crate) focus_listeners: Vec<AnyFocusListener>,
     pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
     propagate_event: bool,
+    default_prevented: bool,
     mouse_position: Point<Pixels>,
     scale_factor: f32,
     pub(crate) scene_builder: SceneBuilder,
@@ -230,6 +231,7 @@ impl Window {
             focus_parents_by_child: HashMap::default(),
             focus_listeners: Vec::new(),
             propagate_event: true,
+            default_prevented: true,
             mouse_position,
             scale_factor,
             scene_builder: SceneBuilder::new(),
@@ -447,6 +449,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.propagate_event = false;
     }
 
+    pub fn prevent_default(&mut self) {
+        self.window.default_prevented = true;
+    }
+
+    pub fn default_prevented(&self) -> bool {
+        self.window.default_prevented
+    }
+
     pub fn on_mouse_event<Event: 'static>(
         &mut self,
         handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static,
@@ -837,6 +847,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 self.window.mouse_position = *position;
             }
 
+            // Handlers may set this to false by calling `stop_propagation`
+            self.window.propagate_event = true;
+            self.window.default_prevented = false;
+
             if let Some(mut handlers) = self
                 .window
                 .mouse_listeners
@@ -845,9 +859,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 // Because handlers may add other handlers, we sort every time.
                 handlers.sort_by(|(a, _), (b, _)| a.cmp(b));
 
-                // Handlers may set this to false by calling `stop_propagation`
-                self.window.propagate_event = true;
-
                 // Capture phase, events bubble from back to front. Handlers for this phase are used for
                 // special purposes, such as detecting events outside of a given Bounds.
                 for (_, handler) in &handlers {

crates/storybook2/src/stories.rs 🔗

@@ -1,7 +1,9 @@
+mod focus;
 mod kitchen_sink;
 mod text;
 mod z_index;
 
+pub use focus::*;
 pub use kitchen_sink::*;
 pub use text::*;
 pub use z_index::*;

crates/storybook2/src/stories/focus.rs 🔗

@@ -0,0 +1,80 @@
+use gpui3::{div, view, Context, Focus, ParentElement, Styled, View, WindowContext};
+
+use crate::themes::rose_pine;
+
+pub struct FocusStory {
+    text: View<()>,
+}
+
+impl FocusStory {
+    pub fn view(cx: &mut WindowContext) -> View<()> {
+        let theme = rose_pine();
+
+        let color_1 = theme.lowest.negative.default.foreground;
+        let color_2 = theme.lowest.positive.default.foreground;
+        let color_3 = theme.lowest.warning.default.foreground;
+        let color_4 = theme.lowest.accent.default.foreground;
+        let color_5 = theme.lowest.variant.default.foreground;
+        let color_6 = theme.highest.negative.default.foreground;
+
+        let parent = cx.focus_handle();
+        let child_1 = cx.focus_handle();
+        let child_2 = cx.focus_handle();
+        view(cx.entity(|cx| ()), move |_, cx| {
+            div()
+                .focusable(&parent)
+                .on_focus(|_, _, _| println!("Parent focused"))
+                .on_blur(|_, _, _| println!("Parent blurred"))
+                .on_focus_in(|_, _, _| println!("Parent focus_in"))
+                .on_focus_out(|_, _, _| println!("Parent focus_out"))
+                .on_key_down(|_, event, phase, _| {
+                    println!("Key down on parent {:?} {:?}", phase, event)
+                })
+                .on_key_up(|_, event, phase, _| {
+                    println!("Key up on parent {:?} {:?}", phase, event)
+                })
+                .size_full()
+                .bg(color_1)
+                .focus(|style| style.bg(color_2))
+                .focus_in(|style| style.bg(color_3))
+                .child(
+                    div()
+                        .focusable(&child_1)
+                        .w_full()
+                        .h_6()
+                        .bg(color_4)
+                        .focus(|style| style.bg(color_5))
+                        .in_focus(|style| style.bg(color_6))
+                        .on_focus(|_, _, _| println!("Child 1 focused"))
+                        .on_blur(|_, _, _| println!("Child 1 blurred"))
+                        .on_focus_in(|_, _, _| println!("Child 1 focus_in"))
+                        .on_focus_out(|_, _, _| println!("Child 1 focus_out"))
+                        .on_key_down(|_, event, phase, _| {
+                            println!("Key down on child 1 {:?} {:?}", phase, event)
+                        })
+                        .on_key_up(|_, event, phase, _| {
+                            println!("Key up on child 1 {:?} {:?}", phase, event)
+                        })
+                        .child("Child 1"),
+                )
+                .child(
+                    div()
+                        .focusable(&child_2)
+                        .w_full()
+                        .h_6()
+                        .bg(color_4)
+                        .on_focus(|_, _, _| println!("Child 2 focused"))
+                        .on_blur(|_, _, _| println!("Child 2 blurred"))
+                        .on_focus_in(|_, _, _| println!("Child 2 focus_in"))
+                        .on_focus_out(|_, _, _| println!("Child 2 focus_out"))
+                        .on_key_down(|_, event, phase, _| {
+                            println!("Key down on child 2 {:?} {:?}", phase, event)
+                        })
+                        .on_key_up(|_, event, phase, _| {
+                            println!("Key up on child 2 {:?} {:?}", phase, event)
+                        })
+                        .child("Child 2"),
+                )
+        })
+    }
+}

crates/storybook2/src/story_selector.rs 🔗

@@ -15,6 +15,7 @@ pub enum ElementStory {
     Avatar,
     Button,
     Details,
+    Focus,
     Icon,
     Input,
     Label,
@@ -35,6 +36,7 @@ impl ElementStory {
                 ui::DetailsStory::new().into_any()
             })
             .into_any(),
+            Self::Focus => FocusStory::view(cx).into_any(),
             Self::Icon => {
                 view(cx.entity(|cx| ()), |_, _| ui::IconStory::new().into_any()).into_any()
             }