settings_ui: Fix page scroll bar lagging behind when jumping to a section (#39897)
The issue was caused by the scroll handle taking a couple of frames to update its offset correctly after calling `ScrollHandle::scroll_to_top_of_item`. The fast fix is forcing 3 frames to render back-to-back. In the future, we should look into `ScrollHandle` and see if there's any way to update its state outside of paint. Release Notes: - N/A Co-authored-by: Danilo Leal <daniloleal09@gmail.com> Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com> Co-authored-by: Katie Geer <katie@zed.dev> Co-authored-by: Ben Kunkle <ben@zed.dev>
This commit is contained in:
co-authored by
Danilo Leal
Mikayla Maki
Katie Geer
Ben Kunkle
parent
98d4c34199
commit
e4f0fbbf80
@@ -558,10 +558,10 @@ pub struct SettingsWindow {
|
||||
/// Index into navbar_entries
|
||||
navbar_entry: usize,
|
||||
navbar_entries: Vec<NavBarEntry>,
|
||||
list_handle: UniformListScrollHandle,
|
||||
navbar_scroll_handle: UniformListScrollHandle,
|
||||
search_matches: Vec<Vec<bool>>,
|
||||
content_handles: Vec<Vec<Entity<NonFocusableHandle>>>,
|
||||
scroll_handle: ScrollHandle,
|
||||
page_scroll_handle: ScrollHandle,
|
||||
focus_handle: FocusHandle,
|
||||
navbar_focus_handle: Entity<NonFocusableHandle>,
|
||||
content_focus_handle: Entity<NonFocusableHandle>,
|
||||
@@ -923,12 +923,12 @@ impl SettingsWindow {
|
||||
pages: vec![],
|
||||
navbar_entries: vec![],
|
||||
navbar_entry: 0,
|
||||
list_handle: UniformListScrollHandle::default(),
|
||||
navbar_scroll_handle: UniformListScrollHandle::default(),
|
||||
search_bar,
|
||||
search_task: None,
|
||||
search_matches: vec![],
|
||||
content_handles: vec![],
|
||||
scroll_handle: ScrollHandle::new(),
|
||||
page_scroll_handle: ScrollHandle::new(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
navbar_focus_handle: NonFocusableHandle::new(
|
||||
NAVBAR_CONTAINER_TAB_INDEX,
|
||||
@@ -1471,7 +1471,7 @@ impl SettingsWindow {
|
||||
let Some(previous_root_index) = root_index else {
|
||||
return;
|
||||
};
|
||||
this.focus_and_scroll_to_nav_entry(previous_root_index, window);
|
||||
this.focus_and_scroll_to_nav_entry(previous_root_index, window, cx);
|
||||
}),
|
||||
)
|
||||
.on_action(cx.listener(|this, _: &FocusNextRootNavEntry, window, cx| {
|
||||
@@ -1491,16 +1491,16 @@ impl SettingsWindow {
|
||||
let Some(next_root_index) = root_index else {
|
||||
return;
|
||||
};
|
||||
this.focus_and_scroll_to_nav_entry(next_root_index, window);
|
||||
this.focus_and_scroll_to_nav_entry(next_root_index, window, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &FocusFirstNavEntry, window, _| {
|
||||
.on_action(cx.listener(|this, _: &FocusFirstNavEntry, window, cx| {
|
||||
if let Some((first_entry_index, _)) = this.visible_navbar_entries().next() {
|
||||
this.focus_and_scroll_to_nav_entry(first_entry_index, window);
|
||||
this.focus_and_scroll_to_nav_entry(first_entry_index, window, cx);
|
||||
}
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &FocusLastNavEntry, window, _| {
|
||||
.on_action(cx.listener(|this, _: &FocusLastNavEntry, window, cx| {
|
||||
if let Some((last_entry_index, _)) = this.visible_navbar_entries().last() {
|
||||
this.focus_and_scroll_to_nav_entry(last_entry_index, window);
|
||||
this.focus_and_scroll_to_nav_entry(last_entry_index, window, cx);
|
||||
}
|
||||
}))
|
||||
.border_color(cx.theme().colors().border)
|
||||
@@ -1551,9 +1551,9 @@ impl SettingsWindow {
|
||||
}),
|
||||
)
|
||||
.size_full()
|
||||
.track_scroll(self.list_handle.clone()),
|
||||
.track_scroll(self.navbar_scroll_handle.clone()),
|
||||
)
|
||||
.vertical_scrollbar_for(self.list_handle.clone(), window, cx),
|
||||
.vertical_scrollbar_for(self.navbar_scroll_handle.clone(), window, cx),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
@@ -1592,7 +1592,7 @@ impl SettingsWindow {
|
||||
return;
|
||||
};
|
||||
self.focus_content_element(first_item_index, window, cx);
|
||||
self.scroll_handle.set_offset(point(px(0.), px(0.)));
|
||||
self.page_scroll_handle.set_offset(point(px(0.), px(0.)));
|
||||
} else {
|
||||
let entry_item_index = self.navbar_entries[navbar_entry_index]
|
||||
.item_index
|
||||
@@ -1603,10 +1603,23 @@ impl SettingsWindow {
|
||||
else {
|
||||
return;
|
||||
};
|
||||
self.scroll_handle
|
||||
self.page_scroll_handle
|
||||
.scroll_to_top_of_item(selected_item_index);
|
||||
self.focus_content_element(entry_item_index, window, cx);
|
||||
}
|
||||
|
||||
// Page scroll handle updates the active item index
|
||||
// in it's next paint call after using scroll_handle.scroll_to_top_of_item
|
||||
// The call after that updates the offset of the scroll handle. So to
|
||||
// ensure the scroll handle doesn't lag behind we need to render three frames
|
||||
// back to back.
|
||||
cx.on_next_frame(window, |_, window, cx| {
|
||||
cx.on_next_frame(window, |_, _, cx| {
|
||||
cx.notify();
|
||||
});
|
||||
cx.notify();
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn is_nav_entry_visible(&self, nav_entry_index: usize) -> bool {
|
||||
@@ -1614,16 +1627,22 @@ impl SettingsWindow {
|
||||
.any(|(index, _)| index == nav_entry_index)
|
||||
}
|
||||
|
||||
fn focus_and_scroll_to_nav_entry(&self, nav_entry_index: usize, window: &mut Window) {
|
||||
fn focus_and_scroll_to_nav_entry(
|
||||
&self,
|
||||
nav_entry_index: usize,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(position) = self
|
||||
.visible_navbar_entries()
|
||||
.position(|(index, _)| index == nav_entry_index)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
self.list_handle
|
||||
self.navbar_scroll_handle
|
||||
.scroll_to_item(position, gpui::ScrollStrategy::Top);
|
||||
window.focus(&self.navbar_entries[nav_entry_index].focus_handle);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn visible_page_items(&self) -> impl Iterator<Item = (usize, &SettingsPageItem)> {
|
||||
@@ -1670,7 +1689,7 @@ impl SettingsWindow {
|
||||
.id("settings-ui-page")
|
||||
.size_full()
|
||||
.overflow_y_scroll()
|
||||
.track_scroll(&self.scroll_handle);
|
||||
.track_scroll(&self.page_scroll_handle);
|
||||
|
||||
let items: Vec<_> = items.collect();
|
||||
let items_len = items.len();
|
||||
@@ -1785,7 +1804,7 @@ impl SettingsWindow {
|
||||
.px_8()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(page_header)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.vertical_scrollbar_for(self.page_scroll_handle.clone(), window, cx)
|
||||
.track_focus(&self.content_focus_handle.focus_handle(cx))
|
||||
.child(
|
||||
div()
|
||||
@@ -2026,7 +2045,7 @@ impl Render for SettingsWindow {
|
||||
{
|
||||
this.open_and_scroll_to_navbar_entry(this.navbar_entry, window, cx);
|
||||
} else {
|
||||
this.focus_and_scroll_to_nav_entry(this.navbar_entry, window);
|
||||
this.focus_and_scroll_to_nav_entry(this.navbar_entry, window, cx);
|
||||
}
|
||||
}))
|
||||
.on_action(
|
||||
@@ -2474,11 +2493,11 @@ mod test {
|
||||
search_bar: cx.new(|cx| Editor::single_line(window, cx)),
|
||||
navbar_entry: selected_idx.expect("Must have a selected navbar entry"),
|
||||
navbar_entries: Vec::default(),
|
||||
list_handle: UniformListScrollHandle::default(),
|
||||
navbar_scroll_handle: UniformListScrollHandle::default(),
|
||||
search_matches: vec![],
|
||||
content_handles: vec![],
|
||||
search_task: None,
|
||||
scroll_handle: ScrollHandle::new(),
|
||||
page_scroll_handle: ScrollHandle::new(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
navbar_focus_handle: NonFocusableHandle::new(
|
||||
NAVBAR_CONTAINER_TAB_INDEX,
|
||||
|
||||
Reference in New Issue
Block a user