editor: Fix pane's tab buttons flicker on right-click (#42549)

Whenever right-click was used on the editor, the pane's tab buttons
would flicker, which was confirmed to happen because of the following
check:

```
self.focus_handle.contains_focused(window, cx)
    || self
        .active_item()
        .is_some_and(|item| {
            item.item_focus_handle(cx).contains_focused(window, cx)
        })
```

This check was returning `false` right after right-clicking but
returning `true` right after. When digging into it a little bit more,
this appears to be happening because the editor's `MouseContextMenu`
relies on `ContextMenu` which is rendered in a deferred fashion but
`MouseContextMenu` updates the window's focus to it instantaneously.

Since the `ContextMenu` is rendered in a deferred fashion, its focus
handle is not yet a descendant of the editor (pane's active item) focus
handle, so the `contains_focused(window, cx)` call would return `false`,
with it returning `true` after the menu was rendered.

This commit updates the `MouseContextMenu::new` function to leverage
`cx.on_next_frame` and ensure that the focus is only moved to the
`ContextMenu` 2 frames later, ensuring that by the time the focus is
moved, the `ContextMenu`'s focus handle is a descendant of the editor's.

Closes #41771 

Release Notes:

- Fixed pane's tab buttons flickering when using right-click on the
editor
This commit is contained in:
Dino
2025-11-13 15:57:26 +00:00
committed by GitHub
parent b6972d70a5
commit b709996ec6
+24 -2
View File
@@ -81,7 +81,19 @@ impl MouseContextMenu {
cx: &mut Context<Editor>,
) -> Self {
let context_menu_focus = context_menu.focus_handle(cx);
window.focus(&context_menu_focus);
// Since `ContextMenu` is rendered in a deferred fashion its focus
// handle is not linked to the Editor's 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 editor's focus handle returns
// `true` when the `ContextMenu` is focused.
let focus_handle = context_menu_focus.clone();
cx.on_next_frame(window, move |_, window, cx| {
cx.on_next_frame(window, move |_, window, _cx| {
window.focus(&focus_handle);
});
});
let _dismiss_subscription = cx.subscribe_in(&context_menu, window, {
let context_menu_focus = context_menu_focus.clone();
@@ -329,8 +341,18 @@ mod tests {
}
"});
cx.editor(|editor, _window, _app| assert!(editor.mouse_context_menu.is_none()));
cx.update_editor(|editor, window, cx| {
deploy_context_menu(editor, Some(Default::default()), point, window, cx)
deploy_context_menu(editor, Some(Default::default()), point, window, cx);
// Assert that, even after deploying the editor's mouse context
// menu, the editor's focus handle still contains the focused
// element. The pane's tab bar relies on this to determine whether
// to show the tab bar buttons and there was a small flicker when
// deploying the mouse context menu that would cause this to not be
// true, making it so that the buttons would disappear for a couple
// of frames.
assert!(editor.focus_handle.contains_focused(window, cx));
});
cx.assert_editor_state(indoc! {"