Fix slow focus transitions to the terminal panel (#24172)

Mikayla Maki created

This long standing bug was caused by `Pane`'s focus_in handler bouncing
the focus to another handle.
Because focus resolution happens _after_ a frame has been rendered, the
only way to deal with this case is to schedule another frame to be
redrawn. However, we where suppressing all window refreshes that occur
during a focus transfer, causing this focus change to be completely
missed. However, changing this behavior can lead to infinite notify
loops, due to drawing a frame causing another to be rendered.

This PR fixes this problem narrowly by adding an `on_next_frame()`
callback in the pane's focus handle, so that the focus changes take
effect almost immediately. But only for this case, where we know it
doesn't cause infinite notify loops.

TODO:
- [x] Fix the infinite notify loop bug or determine a third way to fix
this lag

Release Notes:

- Fixed a bug where shifting focus to the terminal panel could be slow

Change summary

crates/gpui/src/window.rs    | 4 ++--
crates/workspace/src/pane.rs | 5 +++++
2 files changed, 7 insertions(+), 2 deletions(-)

Detailed changes

crates/gpui/src/window.rs 🔗

@@ -137,7 +137,7 @@ impl WindowInvalidator {
         self.inner.borrow_mut().dirty_views = views;
     }
 
-    pub fn not_painting(&self) -> bool {
+    pub fn not_drawing(&self) -> bool {
         self.inner.borrow().draw_phase == DrawPhase::None
     }
 
@@ -1035,7 +1035,7 @@ impl Window {
 
     /// Mark the window as dirty, scheduling it to be redrawn on the next frame.
     pub fn refresh(&mut self) {
-        if self.invalidator.not_painting() {
+        if self.invalidator.not_drawing() {
             self.refreshing = true;
             self.invalidator.set_dirty(true);
         }

crates/workspace/src/pane.rs 🔗

@@ -582,6 +582,11 @@ impl Pane {
 
         if let Some(active_item) = self.active_item() {
             if self.focus_handle.is_focused(window) {
+                // Schedule a redraw next frame, so that the focus changes below take effect
+                cx.on_next_frame(window, |_, _, cx| {
+                    cx.notify();
+                });
+
                 // Pane was focused directly. We need to either focus a view inside the active item,
                 // or focus the active item itself
                 if let Some(weak_last_focus_handle) =