From 7ecad2bef99122cbb796ad1d30243c28dd83822f Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Thu, 12 Jun 2025 11:54:44 +0200 Subject: [PATCH] gpui: Fix window cursor style flickering (#32596) Closes #32592 Follow-up to #31965 This PR fixes the cursor style flickering on Linux systems. The issue arose since the window cursor style was not reused anymore for subsequent frames after the changes in #31965. This works on MacOS for hiding cursors, since they are hidden until the next mouse movement occurs, which is not the case for other systems. This PR re-adds this whilst keeping the fixes applied in #31965. We now determine the first cursor style that is hovered and continue searching for a cursor style that should be applied globally. If one to apply for the whole window is found, we return that cursor style early instead. Alternatively, we could store window cursor style request in a vector similar to normal cursor styles. That would require more memory in exchange for fewer checks which cursor style to apply. I preferred the approach here, though, but can change this should the other method be preferred. CC @smitbarmase since you assigned yourself that issue. Release Notes: - Fixed an issue where the cursor would flicker whilst typing. --- crates/gpui/src/window.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 8253320898..924692ea91 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -24,6 +24,8 @@ use core_video::pixel_buffer::CVPixelBuffer; use derive_more::{Deref, DerefMut}; use futures::FutureExt; use futures::channel::oneshot; +use itertools::FoldWhile::{Continue, Done}; +use itertools::Itertools; use parking_lot::RwLock; use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle}; use refineable::Refineable; @@ -408,7 +410,7 @@ pub(crate) type AnyMouseListener = #[derive(Clone)] pub(crate) struct CursorStyleRequest { - pub(crate) hitbox_id: HitboxId, + pub(crate) hitbox_id: Option, pub(crate) style: CursorStyle, } @@ -622,7 +624,6 @@ pub(crate) struct Frame { pub(crate) input_handlers: Vec>, pub(crate) tooltip_requests: Vec>, pub(crate) cursor_styles: Vec, - window_cursor_style: Option, #[cfg(any(test, feature = "test-support"))] pub(crate) debug_bounds: FxHashMap>, #[cfg(any(feature = "inspector", debug_assertions))] @@ -667,7 +668,6 @@ impl Frame { input_handlers: Vec::new(), tooltip_requests: Vec::new(), cursor_styles: Vec::new(), - window_cursor_style: None, #[cfg(any(test, feature = "test-support"))] debug_bounds: FxHashMap::default(), @@ -693,7 +693,6 @@ impl Frame { self.window_control_hitboxes.clear(); self.deferred_draws.clear(); self.focus = None; - self.window_cursor_style = None; #[cfg(any(feature = "inspector", debug_assertions))] { @@ -703,14 +702,16 @@ impl Frame { } pub(crate) fn cursor_style(&self, window: &Window) -> Option { - self.window_cursor_style.or_else(|| { - self.cursor_styles.iter().rev().find_map(|request| { - request - .hitbox_id - .is_hovered(window) - .then_some(request.style) + self.cursor_styles + .iter() + .rev() + .fold_while(None, |style, request| match request.hitbox_id { + None => Done(Some(request.style)), + Some(hitbox_id) => Continue( + style.or_else(|| hitbox_id.is_hovered(window).then_some(request.style)), + ), }) - }) + .into_inner() } pub(crate) fn hit_test(&self, position: Point) -> HitTest { @@ -2174,7 +2175,7 @@ impl Window { pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) { self.invalidator.debug_assert_paint(); self.next_frame.cursor_styles.push(CursorStyleRequest { - hitbox_id: hitbox.id, + hitbox_id: Some(hitbox.id), style, }); } @@ -2185,7 +2186,10 @@ impl Window { /// phase of element drawing. pub fn set_window_cursor_style(&mut self, style: CursorStyle) { self.invalidator.debug_assert_paint(); - self.next_frame.window_cursor_style = Some(style); + self.next_frame.cursor_styles.push(CursorStyleRequest { + hitbox_id: None, + style, + }) } /// Sets a tooltip to be rendered for the upcoming frame. This method should only be called