Avoid flicker when toggling project browser on workspace open

Antonio Scandurra created

Change summary

crates/gpui/src/app.rs            | 12 ++++++
crates/workspace/src/sidebar.rs   | 21 ++++++----
crates/workspace/src/workspace.rs | 66 +++++++++++++++++----------------
3 files changed, 58 insertions(+), 41 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -566,6 +566,18 @@ impl AsyncAppContext {
         self.update(|cx| cx.add_view(window_id, build_view))
     }
 
+    pub fn add_window<T, F>(
+        &mut self,
+        window_options: WindowOptions,
+        build_root_view: F,
+    ) -> (usize, ViewHandle<T>)
+    where
+        T: View,
+        F: FnOnce(&mut ViewContext<T>) -> T,
+    {
+        self.update(|cx| cx.add_window(window_options, build_root_view))
+    }
+
     pub fn platform(&self) -> Arc<dyn Platform> {
         self.0.borrow().platform()
     }

crates/workspace/src/sidebar.rs 🔗

@@ -8,7 +8,8 @@ pub struct Sidebar {
     side: Side,
     items: Vec<Item>,
     active_item_ix: Option<usize>,
-    width: Rc<RefCell<f32>>,
+    actual_width: Rc<RefCell<f32>>,
+    custom_width: Rc<RefCell<f32>>,
 }
 
 #[derive(Clone, Copy, Deserialize)]
@@ -42,7 +43,8 @@ impl Sidebar {
             side,
             items: Default::default(),
             active_item_ix: None,
-            width: Rc::new(RefCell::new(260.)),
+            actual_width: Rc::new(RefCell::new(260.)),
+            custom_width: Rc::new(RefCell::new(260.)),
         }
     }
 
@@ -137,12 +139,12 @@ impl Sidebar {
             container.add_child(
                 Hook::new(
                     ConstrainedBox::new(ChildView::new(active_item).boxed())
-                        .with_max_width(*self.width.borrow())
+                        .with_max_width(*self.custom_width.borrow())
                         .boxed(),
                 )
                 .on_after_layout({
-                    let width = self.width.clone();
-                    move |size, _| *width.borrow_mut() = size.x()
+                    let actual_width = self.actual_width.clone();
+                    move |size, _| *actual_width.borrow_mut() = size.x()
                 })
                 .flex(1., false)
                 .boxed(),
@@ -157,7 +159,8 @@ impl Sidebar {
     }
 
     fn render_resize_handle(&self, theme: &Theme, cx: &mut RenderContext<Workspace>) -> ElementBox {
-        let width = self.width.clone();
+        let actual_width = self.actual_width.clone();
+        let custom_width = self.custom_width.clone();
         let side = self.side;
         MouseEventHandler::new::<Self, _, _>(side as usize, cx, |_, _| {
             Container::new(Empty::new().boxed())
@@ -171,10 +174,10 @@ impl Sidebar {
         })
         .with_cursor_style(CursorStyle::ResizeLeftRight)
         .on_drag(move |delta, cx| {
-            let prev_width = *width.borrow();
+            let prev_width = *actual_width.borrow();
             match side {
-                Side::Left => *width.borrow_mut() = 0f32.max(prev_width + delta.x()),
-                Side::Right => *width.borrow_mut() = 0f32.max(prev_width - delta.x()),
+                Side::Left => *custom_width.borrow_mut() = 0f32.max(prev_width + delta.x()),
+                Side::Right => *custom_width.borrow_mut() = 0f32.max(prev_width - delta.x()),
             }
 
             cx.notify();

crates/workspace/src/workspace.rs 🔗

@@ -2141,41 +2141,43 @@ pub fn open_paths(
         }
     }
 
-    let is_new_workspace = existing.is_none();
-    let workspace = existing.unwrap_or_else(|| {
-        cx.add_window((app_state.build_window_options)(), |cx| {
-            let project = Project::local(
-                app_state.client.clone(),
-                app_state.user_store.clone(),
-                app_state.languages.clone(),
-                app_state.fs.clone(),
-                cx,
-            );
-            (app_state.build_workspace)(project, &app_state, cx)
-        })
-        .1
-    });
-
-    let task = workspace.update(cx, |workspace, cx| {
-        workspace.open_paths(abs_paths.to_vec(), cx)
-    });
+    let app_state = app_state.clone();
+    let abs_paths = abs_paths.to_vec();
     cx.spawn(|mut cx| async move {
-        let items = task.await;
-        let opened_dir = items.iter().any(|item| item.is_none());
-
-        // Toggle project browser when opening a new workspace that contains a directory.
-        if is_new_workspace && opened_dir {
-            workspace.update(&mut cx, |workspace, cx| {
-                workspace.toggle_sidebar_item(
-                    &ToggleSidebarItem(SidebarItemId {
-                        side: Side::Left,
-                        item_index: 0,
-                    }),
+        let workspace = if let Some(existing) = existing {
+            existing
+        } else {
+            let contains_directory =
+                futures::future::join_all(abs_paths.iter().map(|path| app_state.fs.is_file(path)))
+                    .await
+                    .contains(&false);
+
+            cx.add_window((app_state.build_window_options)(), |cx| {
+                let project = Project::local(
+                    app_state.client.clone(),
+                    app_state.user_store.clone(),
+                    app_state.languages.clone(),
+                    app_state.fs.clone(),
                     cx,
                 );
-                cx.focus_self();
-            });
-        }
+                let mut workspace = (app_state.build_workspace)(project, &app_state, cx);
+                if contains_directory {
+                    workspace.toggle_sidebar_item(
+                        &ToggleSidebarItem(SidebarItemId {
+                            side: Side::Left,
+                            item_index: 0,
+                        }),
+                        cx,
+                    );
+                }
+                workspace
+            })
+            .1
+        };
+
+        let items = workspace
+            .update(&mut cx, |workspace, cx| workspace.open_paths(abs_paths, cx))
+            .await;
         (workspace, items)
     })
 }