From a490e2ea43c09b78b39320f956bdc2e7e07ebc24 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 10 Oct 2026 23:54:13 +0800 Subject: [PATCH 1/2] base: read the pointer only under hovered text, and ask no frame for a closed popup Two things that kept a long list of rendered markdown, as an AI chat transcript is, from being drawn from last frame under gpui-fast's retained views: - Inline text read the pointer's position on every paint to find the link under it, which made every paragraph depend on every pointer move: a scrollbar drag or any mouse move drew the whole transcript again. It now reads the pointer only when its own hitbox is hovered; text not under the pointer has no link under it. - A popup asked for an animation frame the first time it measured its trigger, so that its content could be placed on the next frame. A closed popup has no content to place, and every row coming into view with one asked for a frame, which keeps a list off its scroll layer. It now asks only when it has content; opened later, it places the content at once from the bounds it already noted. --- crates/base/src/popup.rs | 61 +++++++++++++++++++++++++++++++++- crates/base/src/text/inline.rs | 13 ++++++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/crates/base/src/popup.rs b/crates/base/src/popup.rs index ad189fe236..0529c13926 100644 --- a/crates/base/src/popup.rs +++ b/crates/base/src/popup.rs @@ -140,6 +140,9 @@ impl RenderOnce for Popup { offset, ))); + // A closed popup places nothing: it notes its trigger's bounds, for when + // it opens, without asking for another frame. + let has_content = self.content.is_some(); let root = self .base .child(self.trigger) @@ -158,7 +161,7 @@ impl RenderOnce for Popup { state.captured = true; first }); - if first { + if first && has_content { window.request_animation_frame(); } } @@ -307,6 +310,62 @@ mod tests { assert!(background_hovered.get()); } + /// A popup that may open later, rendering is counted. + struct ClosedHarness { + open: bool, + renders: Rc>, + } + + impl Render for ClosedHarness { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + self.renders.set(self.renders.get() + 1); + let popup = Popup::new( + "popup", + div() + .debug_selector(|| "popup-trigger".into()) + .size(px(100.)), + ); + if self.open { + popup.content( + div() + .debug_selector(|| "popup-content".into()) + .size(px(20.)), + ) + } else { + popup + } + } + } + + /// A closed popup notes its trigger's bounds without asking for another + /// frame: a list of rows holding closed popups is not drawn again for + /// each row that shows. Opened, it places its content at once. + #[gpui::test] + fn a_closed_popup_asks_for_no_frame_and_opens_at_once(cx: &mut gpui::TestAppContext) { + let renders = Rc::new(Cell::new(0)); + let (view, window) = cx.add_window_view({ + let renders = renders.clone(); + move |_, _| ClosedHarness { + open: false, + renders, + } + }); + window.update(|window, cx| window.draw(cx).clear(cx)); + cx.run_until_parked(); + window.update(|window, cx| window.draw(cx).clear(cx)); + assert_eq!(renders.get(), 1, "no frame was asked for"); + + view.update(window, |harness, cx| { + harness.open = true; + cx.notify(); + }); + window.update(|window, cx| window.draw(cx).clear(cx)); + assert_eq!( + window.debug_bounds("popup-content").unwrap().size, + gpui::Size::new(px(20.), px(20.)) + ); + } + #[gpui::test] fn trigger_capture_enables_deferred_content_on_the_next_frame(cx: &mut gpui::TestAppContext) { let (_, window) = cx.add_window_view(|_, _| Harness); diff --git a/crates/base/src/text/inline.rs b/crates/base/src/text/inline.rs index b184f0b78d..38e8e40508 100644 --- a/crates/base/src/text/inline.rs +++ b/crates/base/src/text/inline.rs @@ -992,9 +992,16 @@ impl Element for Inline { window.set_cursor_style(CursorStyle::IBeam, &hitbox); } - // link cursor pointer - let hovered_link = - Self::link_range_for_position(&text_layout, &self.links, window.mouse_position()); + // link cursor pointer. Only text under the pointer reads where the pointer + // is: under GPUI's retained views, reading it makes the text depend on + // every pointer move, so a transcript of markdown would be drawn again + // whenever the pointer moved anywhere. + let hovered_link = hitbox + .is_hovered(window) + .then(|| { + Self::link_range_for_position(&text_layout, &self.links, window.mouse_position()) + }) + .flatten(); if hovered_link.is_some() { window.set_cursor_style(CursorStyle::PointingHand, &hitbox); } From 8369ed8c61e0a7f64ad8c904e7a79dafb65095b8 Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 12 Oct 2026 13:08:10 +0800 Subject: [PATCH 2/2] base: test a closed popup by the frames it asks for, not its renders a_closed_popup_asks_for_no_frame_and_opens_at_once did not compile: it called cx.run_until_parked() while the window it had added still borrowed cx. Compiled, it still could not tell the change apart: it counted the root view's renders, and upstream GPUI renders the root view on every draw, so the count was three with or without the popup asking for a frame. It now asks the window how many next-frame callbacks the first draw left (Window::simulate_next_frame): none for a closed popup, one if it still asks for a frame. --- crates/base/src/popup.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/crates/base/src/popup.rs b/crates/base/src/popup.rs index 0529c13926..ef32ca3c8b 100644 --- a/crates/base/src/popup.rs +++ b/crates/base/src/popup.rs @@ -310,15 +310,13 @@ mod tests { assert!(background_hovered.get()); } - /// A popup that may open later, rendering is counted. + /// A popup that may open later. struct ClosedHarness { open: bool, - renders: Rc>, } impl Render for ClosedHarness { fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - self.renders.set(self.renders.get() + 1); let popup = Popup::new( "popup", div() @@ -342,18 +340,11 @@ mod tests { /// each row that shows. Opened, it places its content at once. #[gpui::test] fn a_closed_popup_asks_for_no_frame_and_opens_at_once(cx: &mut gpui::TestAppContext) { - let renders = Rc::new(Cell::new(0)); - let (view, window) = cx.add_window_view({ - let renders = renders.clone(); - move |_, _| ClosedHarness { - open: false, - renders, - } + let (view, window) = cx.add_window_view(|_, _| ClosedHarness { open: false }); + window.update(|window, cx| { + window.draw(cx).clear(cx); + assert_eq!(window.simulate_next_frame(cx), 0, "no frame was asked for"); }); - window.update(|window, cx| window.draw(cx).clear(cx)); - cx.run_until_parked(); - window.update(|window, cx| window.draw(cx).clear(cx)); - assert_eq!(renders.get(), 1, "no frame was asked for"); view.update(window, |harness, cx| { harness.open = true;