From 7eaddbc5d9410254ea05e017a11d6873980d3e2a Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 16 Jul 2026 13:43:27 +0200 Subject: [PATCH] editor: Blink cursors only in the focused editor (#61086) Every editor associated with a window observed window activation. The handler enabled the blink manager for every editor, including hidden and unfocused editors. Their independent 500 ms timers invalidated the window out of phase and caused redundant full-window presents. Window activation and deactivation are already represented as focus transitions by GPUI: an inactive window has an empty effective focus path. Remove the redundant activation observer and rely on the editor focus and blur handlers to stop blinking on deactivation and restart it only for the editor that owns focus on activation. In a 40-second idle diagnostic run before this change, the steady-state tail repeatedly contained invalidations from one or two editors per frame. After the change, no unfocused-editor invalidations appeared. Bounded scrollbar fades and visible activity animations remain unchanged. Add a regression test with focused and unfocused editors that covers activation, deactivation, and reactivation. Release Notes: - Fixed cursors blinking in unfocused editors, reducing idle redraws. --------- Signed-off-by: Daan De Meyer --- crates/editor/src/blink_manager.rs | 5 ++++ crates/editor/src/editor.rs | 10 -------- crates/editor/src/editor_tests.rs | 39 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/blink_manager.rs b/crates/editor/src/blink_manager.rs index f265744de3..f6ebae5a91 100644 --- a/crates/editor/src/blink_manager.rs +++ b/crates/editor/src/blink_manager.rs @@ -112,4 +112,9 @@ impl BlinkManager { pub fn visible(&self) -> bool { self.visible } + + #[cfg(test)] + pub(crate) fn enabled(&self) -> bool { + self.enabled + } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 06c2913c5d..8cbbba449e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2416,16 +2416,6 @@ impl Editor { cx.observe_global_in::(window, Self::settings_changed), cx.observe_global_in::(window, Self::theme_changed), observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()), - cx.observe_window_activation(window, |editor, window, cx| { - let active = window.is_window_active(); - editor.blink_manager.update(cx, |blink_manager, cx| { - if active { - blink_manager.enable(cx); - } else { - blink_manager.disable(cx); - } - }); - }), ] }) .unwrap_or_default(), diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 3efc8a5b29..516797a740 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2485,6 +2485,45 @@ fn test_beginning_of_line_single_line_editor(cx: &mut TestAppContext) { }); } +#[gpui::test] +fn test_only_focused_editor_blinks_across_window_activation(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let window = cx.add_window(|window, cx| Editor::single_line(window, cx)); + let unfocused_editor = window + .update(cx, |focused_editor, window, cx| { + window.focus(&focused_editor.focus_handle(cx), cx); + let unfocused_editor = cx.new(|cx| Editor::single_line(window, cx)); + window.activate_window(); + unfocused_editor + }) + .unwrap(); + let focused_editor = window.root(cx).unwrap(); + let cx = &mut VisualTestContext::from_window(*window, cx); + cx.run_until_parked(); + + cx.update(|window, cx| { + assert!(window.is_window_active()); + assert!(focused_editor.read(cx).blink_manager.read(cx).enabled()); + assert!(!unfocused_editor.read(cx).blink_manager.read(cx).enabled()); + }); + + cx.deactivate_window(); + cx.update(|window, cx| { + assert!(!window.is_window_active()); + assert!(!focused_editor.read(cx).blink_manager.read(cx).enabled()); + assert!(!unfocused_editor.read(cx).blink_manager.read(cx).enabled()); + }); + + cx.update(|window, _| window.activate_window()); + cx.run_until_parked(); + cx.update(|window, cx| { + assert!(window.is_window_active()); + assert!(focused_editor.read(cx).blink_manager.read(cx).enabled()); + assert!(!unfocused_editor.read(cx).blink_manager.read(cx).enabled()); + }); +} + #[gpui::test] fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) { init_test(cx, |_| {});