Fix project panel scrolling position restoration (#8961)

Andrew Lygin created

Project panel loses the last scrolling position every time the user
hides/shows it. This PR fixes the problem.

The reason of the problem is that `UniformListScrollHandle`, which is
intended to store the scrolling position between redrawings, is only
used for ad-hoc autoscrollings to the list items, while the
`interactivity.scroll_handle` that is responsible for the scrolling
position, doesn't survive the project panel hiding.

How the problem looks:


https://github.com/zed-industries/zed/assets/2101250/7c7e3da6-9a9d-4f28-a181-ee9547349d4c

Release Notes:

- Fixed scrolling position restoration in the Project Panel.

Change summary

crates/gpui/src/elements/uniform_list.rs  | 5 ++++-
crates/project_panel/src/project_panel.rs | 8 ++++----
2 files changed, 8 insertions(+), 5 deletions(-)

Detailed changes

crates/gpui/src/elements/uniform_list.rs 🔗

@@ -7,7 +7,7 @@
 use crate::{
     point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
     ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
-    Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
+    Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
 };
 use smallvec::SmallVec;
 use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -74,6 +74,7 @@ pub struct UniformList {
 /// This should be stored in your view and passed to the uniform_list on each frame.
 #[derive(Clone, Default)]
 pub struct UniformListScrollHandle {
+    base_handle: ScrollHandle,
     deferred_scroll_to_item: Rc<RefCell<Option<usize>>>,
 }
 
@@ -81,6 +82,7 @@ impl UniformListScrollHandle {
     /// Create a new scroll handle to bind to a uniform list.
     pub fn new() -> Self {
         Self {
+            base_handle: ScrollHandle::new(),
             deferred_scroll_to_item: Rc::new(RefCell::new(None)),
         }
     }
@@ -299,6 +301,7 @@ impl UniformList {
 
     /// Track and render scroll state of this list with reference to the given scroll handle.
     pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
+        self.interactivity.scroll_handle = Some(handle.base_handle.clone());
         self.scroll_handle = Some(handle);
         self
     }

crates/project_panel/src/project_panel.rs 🔗

@@ -40,7 +40,7 @@ const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX;
 pub struct ProjectPanel {
     project: Model<Project>,
     fs: Arc<dyn Fs>,
-    list: UniformListScrollHandle,
+    scroll_handle: UniformListScrollHandle,
     focus_handle: FocusHandle,
     visible_entries: Vec<(WorktreeId, Vec<Entry>)>,
     last_worktree_root_id: Option<ProjectEntryId>,
@@ -224,7 +224,7 @@ impl ProjectPanel {
             let mut this = Self {
                 project: project.clone(),
                 fs: workspace.app_state().fs.clone(),
-                list: UniformListScrollHandle::new(),
+                scroll_handle: UniformListScrollHandle::new(),
                 focus_handle,
                 visible_entries: Default::default(),
                 last_worktree_root_id: Default::default(),
@@ -864,7 +864,7 @@ impl ProjectPanel {
 
     fn autoscroll(&mut self, cx: &mut ViewContext<Self>) {
         if let Some((_, _, index)) = self.selection.and_then(|s| self.index_for_selection(s)) {
-            self.list.scroll_to_item(index);
+            self.scroll_handle.scroll_to_item(index);
             cx.notify();
         }
     }
@@ -1565,7 +1565,7 @@ impl Render for ProjectPanel {
                         },
                     )
                     .size_full()
-                    .track_scroll(self.list.clone()),
+                    .track_scroll(self.scroll_handle.clone()),
                 )
                 .children(self.context_menu.as_ref().map(|(menu, position, _)| {
                     overlay()