From ddc71653ad080c45f867bdb70d033000804f8d38 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 25 Oct 2022 18:36:33 +0200 Subject: [PATCH 1/3] Maintain scroll position in contacts list Co-Authored-By: Max Brunsfeld --- crates/collab_ui/src/contact_list.rs | 60 ++++++++++++++++++++++++++-- crates/gpui/src/elements/list.rs | 45 ++++++++++++++------- 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 9091e2687bd6f27182532d7709aa8896baf33a24..a4a0b5d18e8ae7ce9507c2fa9424ea26b789fb78 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{mem, sync::Arc}; use crate::contacts_popover; use call::ActiveCall; @@ -334,8 +334,14 @@ impl ContactList { } else if !self.entries.is_empty() { self.selection = Some(0); } - cx.notify(); self.list_state.reset(self.entries.len()); + if let Some(ix) = self.selection { + self.list_state.scroll_to(ListOffset { + item_ix: ix, + offset_in_item: 0., + }); + } + cx.notify(); } fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { @@ -346,8 +352,14 @@ impl ContactList { self.selection = None; } } - cx.notify(); self.list_state.reset(self.entries.len()); + if let Some(ix) = self.selection { + self.list_state.scroll_to(ListOffset { + item_ix: ix, + offset_in_item: 0., + }); + } + cx.notify(); } fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { @@ -404,7 +416,7 @@ impl ContactList { let executor = cx.background().clone(); let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned()); - self.entries.clear(); + let old_entries = mem::take(&mut self.entries); if let Some(room) = ActiveCall::global(cx).read(cx).room() { let room = room.read(cx); @@ -653,7 +665,47 @@ impl ContactList { } } + let old_scroll_top = self.list_state.logical_scroll_top(); self.list_state.reset(self.entries.len()); + + // Attempt to maintain the same scroll position. + if let Some(old_top_entry) = old_entries.get(old_scroll_top.item_ix) { + let new_scroll_top = self + .entries + .iter() + .position(|entry| entry == old_top_entry) + .map(|item_ix| ListOffset { + item_ix, + offset_in_item: old_scroll_top.offset_in_item, + }) + .or_else(|| { + let entry_after_old_top = old_entries.get(old_scroll_top.item_ix + 1)?; + let item_ix = self + .entries + .iter() + .position(|entry| entry == entry_after_old_top)?; + Some(ListOffset { + item_ix, + offset_in_item: 0., + }) + }) + .or_else(|| { + let entry_before_old_top = + old_entries.get(old_scroll_top.item_ix.saturating_sub(1))?; + let item_ix = self + .entries + .iter() + .position(|entry| entry == entry_before_old_top)?; + Some(ListOffset { + item_ix, + offset_in_item: 0., + }) + }); + + self.list_state + .scroll_to(new_scroll_top.unwrap_or(old_scroll_top)); + } + cx.notify(); } diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index a1b4ef0c796ad3a061916b2b2a871fe26bfe4b1a..f1b747d6471230a040fba88859dff75449406aef 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -38,8 +38,8 @@ struct StateInner { #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct ListOffset { - item_ix: usize, - offset_in_item: f32, + pub item_ix: usize, + pub offset_in_item: f32, } #[derive(Clone)] @@ -112,18 +112,7 @@ impl Element for List { let mut new_items = SumTree::new(); let mut rendered_items = VecDeque::new(); let mut rendered_height = 0.; - let mut scroll_top = state - .logical_scroll_top - .unwrap_or_else(|| match state.orientation { - Orientation::Top => ListOffset { - item_ix: 0, - offset_in_item: 0., - }, - Orientation::Bottom => ListOffset { - item_ix: state.items.summary().count, - offset_in_item: 0., - }, - }); + let mut scroll_top = state.logical_scroll_top(); // Render items after the scroll top, including those in the trailing overdraw. let mut cursor = old_items.cursor::(); @@ -421,6 +410,20 @@ impl ListState { ) { self.0.borrow_mut().scroll_handler = Some(Box::new(handler)) } + + pub fn logical_scroll_top(&self) -> ListOffset { + self.0.borrow().logical_scroll_top() + } + + pub fn scroll_to(&self, mut scroll_top: ListOffset) { + let state = &mut *self.0.borrow_mut(); + let item_count = state.items.summary().count; + if scroll_top.item_ix >= item_count { + scroll_top.item_ix = item_count; + scroll_top.offset_in_item = 0.; + } + state.logical_scroll_top = Some(scroll_top); + } } impl StateInner { @@ -514,6 +517,20 @@ impl StateInner { cx.notify(); } + fn logical_scroll_top(&self) -> ListOffset { + self.logical_scroll_top + .unwrap_or_else(|| match self.orientation { + Orientation::Top => ListOffset { + item_ix: 0, + offset_in_item: 0., + }, + Orientation::Bottom => ListOffset { + item_ix: self.items.summary().count, + offset_in_item: 0., + }, + }) + } + fn scroll_top(&self, logical_scroll_top: &ListOffset) -> f32 { let mut cursor = self.items.cursor::(); cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right, &()); From 990c83eabd6b871044661515cb4f55041fe38acd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 25 Oct 2022 18:54:34 +0200 Subject: [PATCH 2/3] Embed live_kit_client's .gitignore into top-level .gitignore Co-authored-by: Max Brunsfeld --- .gitignore | 10 +++++++++- crates/live_kit_client/LiveKitBridge/.gitignore | 9 --------- 2 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 crates/live_kit_client/LiveKitBridge/.gitignore diff --git a/.gitignore b/.gitignore index 93079fad52b245b8a1d85b087d255a4119d11c98..b4eba05582fe7042cb211330b269dc0798acd6c4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,12 @@ /assets/themes/*.json /assets/themes/Internal/*.json /assets/themes/Experiments/*.json -**/venv \ No newline at end of file +**/venv +.build +Packages +*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/crates/live_kit_client/LiveKitBridge/.gitignore b/crates/live_kit_client/LiveKitBridge/.gitignore deleted file mode 100644 index 3b29812086f28a2b21884e57ead495ffd9434178..0000000000000000000000000000000000000000 --- a/crates/live_kit_client/LiveKitBridge/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -/.build -/Packages -/*.xcodeproj -xcuserdata/ -DerivedData/ -.swiftpm/config/registries.json -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -.netrc From 04477e9f973827f849c742073627a823db462d18 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 25 Oct 2022 19:31:58 +0200 Subject: [PATCH 3/3] Explicitly list cargo workspace members This prevents build failures when there are stale subfolders under `crates/`. --- Cargo.toml | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7e3623af98b466ec9f79b9490f2bbd64f8c455cb..205017da1fbc156543b143fc13238780767e7734 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,61 @@ [workspace] -members = ["crates/*"] +members = [ + "crates/activity_indicator", + "crates/assets", + "crates/auto_update", + "crates/breadcrumbs", + "crates/call", + "crates/cli", + "crates/client", + "crates/clock", + "crates/collab", + "crates/collab_ui", + "crates/collections", + "crates/command_palette", + "crates/context_menu", + "crates/db", + "crates/diagnostics", + "crates/drag_and_drop", + "crates/editor", + "crates/file_finder", + "crates/fs", + "crates/fsevent", + "crates/fuzzy", + "crates/git", + "crates/go_to_line", + "crates/gpui", + "crates/gpui_macros", + "crates/journal", + "crates/language", + "crates/live_kit_client", + "crates/live_kit_server", + "crates/lsp", + "crates/media", + "crates/menu", + "crates/outline", + "crates/picker", + "crates/plugin", + "crates/plugin_macros", + "crates/plugin_runtime", + "crates/project", + "crates/project_panel", + "crates/project_symbols", + "crates/rope", + "crates/rpc", + "crates/search", + "crates/settings", + "crates/snippet", + "crates/sum_tree", + "crates/terminal", + "crates/text", + "crates/theme", + "crates/theme_selector", + "crates/theme_testbench", + "crates/util", + "crates/vim", + "crates/workspace", + "crates/zed", +] default-members = ["crates/zed"] resolver = "2"