Fix tab bar button flickering when opening menus (#45098)

Antonio Scandurra created

Closes #33018

### Problem

When opening a `PopoverMenu` or `RightClickMenu`, the pane's tab bar
buttons would flicker (disappear for a couple frames then reappear).
This happened because:

1. The menu is created and `window.focus()` was called immediately
2. However, menus are rendered using `deferred()`, so their focus
handles aren't connected in the dispatch tree until after the deferred
draw callback runs
3. When the pane checks `has_focus()`, it calls `contains_focused()`
which walks up the focus hierarchy — but the menu's focus handle isn't
linked yet
4. `has_focus()` returns false → tab bar buttons disappear
5. Next frame, the menu is rendered and linked → `has_focus()` returns
true → buttons reappear

### Solution

Delay the focus transfer by 2 frames using nested `on_next_frame()`
calls before focusing the menu.

**Why 2 frames instead of 1?**

The frame lifecycle in GPUI runs `next_frame_callbacks` BEFORE `draw()`:

```
on_request_frame:
  1. Run next_frame_callbacks
  2. window.draw()  ← menu rendered here via deferred()
  3. Present
```

So:
- **Frame 1**: First `on_next_frame` callback runs, queues second
callback. Then `draw()` renders the menu and connects its focus handle
to the dispatch tree.
- **Frame 2**: Second `on_next_frame` callback runs and focuses the
menu. Now the focus handle is connected (from Frame 1's draw), so
`contains_focused()` returns true.

With only 1 frame, the focus would happen BEFORE `draw()`, when the
menu's focus handle isn't connected yet.

This follows the same pattern established in b709996ec6 which fixed the
identical issue for the editor's `MouseContextMenu`.

Change summary

crates/ui/src/components/popover_menu.rs     | 14 +++++++++++++-
crates/ui/src/components/right_click_menu.rs | 14 +++++++++++++-
2 files changed, 26 insertions(+), 2 deletions(-)

Detailed changes

crates/ui/src/components/popover_menu.rs 🔗

@@ -287,7 +287,19 @@ fn show_menu<M: ManagedView>(
             window.refresh();
         })
         .detach();
-    window.focus(&new_menu.focus_handle(cx));
+
+    // Since menus are rendered in a deferred fashion, their focus handles are
+    // not linked in the dispatch tree until after the deferred draw callback
+    // runs. We need to wait for that to happen before focusing it, so that
+    // calling `contains_focused` on the parent's focus handle returns `true`
+    // when the menu is focused. This prevents the pane's tab bar buttons from
+    // flickering when opening popover menus.
+    let focus_handle = new_menu.focus_handle(cx);
+    window.on_next_frame(move |window, _cx| {
+        window.on_next_frame(move |window, _cx| {
+            window.focus(&focus_handle);
+        });
+    });
     *menu.borrow_mut() = Some(new_menu);
     window.refresh();
 

crates/ui/src/components/right_click_menu.rs 🔗

@@ -259,7 +259,19 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
                                 window.refresh();
                             })
                             .detach();
-                        window.focus(&new_menu.focus_handle(cx));
+
+                        // Since menus are rendered in a deferred fashion, their focus handles are
+                        // not linked in the dispatch tree until after the deferred draw callback
+                        // runs. We need to wait for that to happen before focusing it, so that
+                        // calling `contains_focused` on the parent's focus handle returns `true`
+                        // when the menu is focused. This prevents the pane's tab bar buttons from
+                        // flickering when opening menus.
+                        let focus_handle = new_menu.focus_handle(cx);
+                        window.on_next_frame(move |window, _cx| {
+                            window.on_next_frame(move |window, _cx| {
+                                window.focus(&focus_handle);
+                            });
+                        });
                         *menu.borrow_mut() = Some(new_menu);
                         *position.borrow_mut() = if let Some(child_bounds) = child_bounds {
                             if let Some(attach) = attach {