gpui_kit/data/viewport.rs
1//! Where a virtualized surface is scrolled to.
2//!
3//! A `RenderOnce` builder is rebuilt every frame and cannot carry anything, so
4//! a list, a table, or a tree that only draws its viewport has nowhere of its
5//! own to keep the offset. Keying one scroll handle by the surface's identity
6//! keeps the position across rebuilds without making every caller own a GPUI
7//! handle, and it lets a surface built on top of another one move it by name.
8
9use std::cell::RefCell;
10use std::collections::HashMap;
11
12use gpui::{App, Global, ScrollStrategy, SharedString, UniformListScrollHandle};
13
14use crate::foundation::Ident;
15
16#[derive(Default)]
17struct ScrollHandles(RefCell<HashMap<SharedString, UniformListScrollHandle>>);
18
19impl Global for ScrollHandles {}
20
21/// The scroll position of the surface with this identity.
22pub(crate) fn scroll_handle(ident: &Ident, cx: &mut App) -> UniformListScrollHandle {
23 if !cx.has_global::<ScrollHandles>() {
24 cx.set_global(ScrollHandles::default());
25 }
26 let mut handles = cx.global::<ScrollHandles>().0.borrow_mut();
27 handles.entry(ident.semantic_id()).or_default().clone()
28}
29
30/// Brings row `index` of the surface with this identity to the bottom edge.
31///
32/// Scroll position belongs to the surface, not to whoever draws over it, so a
33/// surface built on a list — a conversation that follows its newest message —
34/// moves it by naming the list rather than by owning a GPUI handle of its own.
35pub fn scroll_to_row(ident: &Ident, index: usize, cx: &mut App) {
36 scroll_handle(ident, cx).scroll_to_item(index, ScrollStrategy::Bottom);
37}
38
39/// Brings row `index` into view by the shortest move that gets it there, and
40/// leaves the offset alone when the row is already on screen.
41pub fn reveal_row(ident: &Ident, index: usize, cx: &mut App) {
42 scroll_handle(ident, cx).scroll_to_item(index, ScrollStrategy::Nearest);
43}