Detailed changes
@@ -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();
+ }
}
})
}
@@ -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)
}
}));
@@ -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 {
@@ -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::*;
@@ -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"),
+ )
+ })
+ }
+}
@@ -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()
}