diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index fa9960c1fb..7d61f4d847 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -569,59 +569,61 @@ impl AppContext { /// such as notifying observers, emitting events, etc. Effects can themselves /// cause effects, so we continue looping until all effects are processed. fn flush_effects(&mut self) { - loop { - self.release_dropped_entities(); - self.release_dropped_focus_handles(); - if let Some(effect) = self.pending_effects.pop_front() { - match effect { - Effect::Notify { emitter } => { - self.apply_notify_effect(emitter); + while !self.pending_effects.is_empty() { + loop { + self.release_dropped_entities(); + self.release_dropped_focus_handles(); + if let Some(effect) = self.pending_effects.pop_front() { + match effect { + Effect::Notify { emitter } => { + self.apply_notify_effect(emitter); + } + Effect::Emit { + emitter, + event_type, + event, + } => self.apply_emit_effect(emitter, event_type, event), + Effect::FocusChanged { + window_handle, + focused, + } => { + self.apply_focus_changed_effect(window_handle, focused); + } + Effect::Refresh => { + self.apply_refresh_effect(); + } + Effect::NotifyGlobalObservers { global_type } => { + self.apply_notify_global_observers_effect(global_type); + } + Effect::Defer { callback } => { + self.apply_defer_effect(callback); + } } - Effect::Emit { - emitter, - event_type, - event, - } => self.apply_emit_effect(emitter, event_type, event), - Effect::FocusChanged { - window_handle, - focused, - } => { - self.apply_focus_changed_effect(window_handle, focused); - } - Effect::Refresh => { - self.apply_refresh_effect(); - } - Effect::NotifyGlobalObservers { global_type } => { - self.apply_notify_global_observers_effect(global_type); - } - Effect::Defer { callback } => { - self.apply_defer_effect(callback); - } - } - } else { - break; - } - } - - for window in self.windows.values() { - if let Some(window) = window.as_ref() { - if window.dirty { - window.platform_window.invalidate(); + } else { + break; } } - } - #[cfg(any(test, feature = "test-support"))] - for window in self - .windows - .values() - .filter_map(|window| { - let window = window.as_ref()?; - window.dirty.then_some(window.handle) - }) - .collect::>() - { - self.update_window(window, |_, cx| cx.draw()).unwrap(); + for window in self.windows.values() { + if let Some(window) = window.as_ref() { + if window.dirty { + window.platform_window.invalidate(); + } + } + } + + #[cfg(any(test, feature = "test-support"))] + for window in self + .windows + .values() + .filter_map(|window| { + let window = window.as_ref()?; + window.dirty.then_some(window.handle) + }) + .collect::>() + { + self.update_window(window, |_, cx| cx.draw()).unwrap(); + } } } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 0b8a7f1263..c94da71d85 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -229,6 +229,7 @@ pub struct Window { pub(crate) next_frame: Frame, pub(crate) focus_handles: Arc>>, pub(crate) focus_listeners: SubscriberSet<(), AnyWindowFocusListener>, + pub(crate) blur_listeners: SubscriberSet<(), AnyObserver>, default_prevented: bool, mouse_position: Point, requested_cursor_style: Option, @@ -362,6 +363,7 @@ impl Window { next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_listeners: SubscriberSet::new(), + blur_listeners: SubscriberSet::new(), default_prevented: true, mouse_position, requested_cursor_style: None, @@ -1235,6 +1237,16 @@ impl<'a> WindowContext<'a> { /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) -> Scene { + let window_was_focused = self + .window + .focus + .and_then(|focus_id| { + self.window + .rendered_frame + .dispatch_tree + .focusable_node_id(focus_id) + }) + .is_some(); self.text_system().start_frame(); self.window.platform_window.clear_input_handler(); self.window.layout_engine.as_mut().unwrap().clear(); @@ -1273,6 +1285,23 @@ impl<'a> WindowContext<'a> { }); } + let window_is_focused = self + .window + .focus + .and_then(|focus_id| { + self.window + .next_frame + .dispatch_tree + .focusable_node_id(focus_id) + }) + .is_some(); + if window_was_focused && !window_is_focused { + self.window + .blur_listeners + .clone() + .retain(&(), |listener| listener(self)); + } + self.window .next_frame .dispatch_tree @@ -2423,23 +2452,16 @@ impl<'a, V: 'static> ViewContext<'a, V> { /// Register a listener to be called when the window loses focus. /// Unlike [on_focus_changed], returns a subscription and persists until the subscription /// is dropped. - pub fn on_window_focus_lost( + pub fn on_blur_window( &mut self, mut listener: impl FnMut(&mut V, &mut ViewContext) + 'static, ) -> Subscription { let view = self.view.downgrade(); - let (subscription, activate) = self.window.focus_listeners.insert( + let (subscription, activate) = self.window.blur_listeners.insert( (), - Box::new(move |event, cx| { - view.update(cx, |view, cx| { - if event.blurred.is_none() && event.focused.is_none() { - listener(view, cx) - } - }) - .is_ok() - }), + Box::new(move |cx| view.update(cx, |view, cx| listener(view, cx)).is_ok()), ); - self.app.defer(move |_| activate()); + activate(); subscription } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 2bcf6ec13e..520eee0d2f 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -532,7 +532,7 @@ impl Workspace { cx.notify() }) .detach(); - cx.on_window_focus_lost(|this, cx| { + cx.on_blur_window(|this, cx| { let focus_handle = this.focus_handle(cx); cx.focus(&focus_handle); })