From b709996ec6e57d7593f409fe519695df7d5e2608 Mon Sep 17 00:00:00 2001 From: Dino Date: Thu, 13 Nov 2025 15:57:26 +0000 Subject: [PATCH] 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 --- crates/editor/src/mouse_context_menu.rs | 26 +++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 2a63e39adda52734b301eda0d32a5bfa10a8e47e..94e5019d59b68a33d2d64245d2d1e17a764638da 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -81,7 +81,19 @@ impl MouseContextMenu { cx: &mut Context, ) -> 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! {"