diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 7fb13fc4cf9164ceefa675f24d4767008b9f818a..195a50d1c20e37c066d46285665d138cd7fae1d1 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -558,10 +558,10 @@ pub struct SettingsWindow { /// Index into navbar_entries navbar_entry: usize, navbar_entries: Vec, - list_handle: UniformListScrollHandle, + navbar_scroll_handle: UniformListScrollHandle, search_matches: Vec>, content_handles: Vec>>, - scroll_handle: ScrollHandle, + page_scroll_handle: ScrollHandle, focus_handle: FocusHandle, navbar_focus_handle: Entity, content_focus_handle: Entity, @@ -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, + ) { 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 { @@ -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,