From d8d0bdc479ce926512697bc04a6c0a485b611ad4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 12:02:30 +0200 Subject: [PATCH 01/35] WIP: git menu --- crates/collab_ui/src/branch_list.rs | 178 +++++++++++++++++++ crates/collab_ui/src/collab_titlebar_item.rs | 123 ++++++++++--- crates/collab_ui/src/collab_ui.rs | 2 + crates/fs/src/repository.rs | 32 +++- 4 files changed, 311 insertions(+), 24 deletions(-) create mode 100644 crates/collab_ui/src/branch_list.rs diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs new file mode 100644 index 0000000000..8d755e8aa4 --- /dev/null +++ b/crates/collab_ui/src/branch_list.rs @@ -0,0 +1,178 @@ +use client::{ContactRequestStatus, User, UserStore}; +use fuzzy::{StringMatch, StringMatchCandidate}; +use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext}; +use picker::{Picker, PickerDelegate, PickerEvent}; +use project::Project; +use std::sync::Arc; +use util::{ResultExt, TryFutureExt}; + +pub fn init(cx: &mut AppContext) { + Picker::::init(cx); +} + +pub type BranchList = Picker; + +pub fn build_branch_list( + project: ModelHandle, + cx: &mut ViewContext, +) -> BranchList { + Picker::new( + BranchListDelegate { + branches: vec!["Foo".into(), "bar/baz".into()], + matches: vec![], + project, + selected_index: 0, + }, + cx, + ) + .with_theme(|theme| theme.picker.clone()) +} + +pub struct BranchListDelegate { + branches: Vec, + matches: Vec, + project: ModelHandle, + selected_index: usize, +} + +impl PickerDelegate for BranchListDelegate { + fn placeholder_text(&self) -> Arc { + "Select branch...".into() + } + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext>) { + self.selected_index = ix; + } + + fn update_matches(&mut self, query: String, cx: &mut ViewContext>) -> Task<()> { + cx.spawn(move |picker, mut cx| async move { + let candidates = picker + .read_with(&mut cx, |view, cx| { + let delegate = view.delegate(); + let project = delegate.project.read(&cx); + let mut cwd = project + .visible_worktrees(cx) + .next() + .unwrap() + .read(cx) + .root_entry() + .unwrap() + .path + .to_path_buf(); + cwd.push(".git"); + let branches = project.fs().open_repo(&cwd).unwrap().lock().branches(); + branches + .unwrap() + .iter() + .cloned() + .enumerate() + .map(|(ix, command)| StringMatchCandidate { + id: ix, + string: command.clone(), + char_bag: command.chars().collect(), + }) + .collect::>() + }) + .unwrap(); + let matches = if query.is_empty() { + candidates + .into_iter() + .enumerate() + .map(|(index, candidate)| StringMatch { + candidate_id: index, + string: candidate.string, + positions: Vec::new(), + score: 0.0, + }) + .collect() + } else { + fuzzy::match_strings( + &candidates, + &query, + true, + 10000, + &Default::default(), + cx.background(), + ) + .await + }; + picker + .update(&mut cx, |picker, _| { + let delegate = picker.delegate_mut(); + //delegate.branches = actions; + delegate.matches = matches; + if delegate.matches.is_empty() { + delegate.selected_index = 0; + } else { + delegate.selected_index = + core::cmp::min(delegate.selected_index, delegate.matches.len() - 1); + } + }) + .log_err(); + }) + } + + fn confirm(&mut self, cx: &mut ViewContext>) { + log::error!("confirm {}", self.selected_index()); + let current_pick = self.selected_index(); + let current_pick = self.matches[current_pick].string.clone(); + log::error!("Hi? {current_pick}"); + let project = self.project.read(cx); + let mut cwd = project + .visible_worktrees(cx) + .next() + .unwrap() + .read(cx) + .root_entry() + .unwrap() + .path + .to_path_buf(); + cwd.push(".git"); + log::error!("{current_pick}"); + project + .fs() + .open_repo(&cwd) + .unwrap() + .lock() + .change_branch(¤t_pick) + .log_err(); + cx.emit(PickerEvent::Dismiss); + } + + fn dismissed(&mut self, cx: &mut ViewContext>) { + cx.emit(PickerEvent::Dismiss); + } + + fn render_match( + &self, + ix: usize, + mouse_state: &mut MouseState, + selected: bool, + cx: &gpui::AppContext, + ) -> AnyElement> { + let theme = &theme::current(cx); + let user = &self.matches[ix]; + let style = theme.picker.item.in_state(selected).style_for(mouse_state); + Flex::row() + .with_child( + Label::new(user.string.clone(), style.label.clone()) + .with_highlights(user.positions.clone()) + .contained() + .aligned() + .left(), + ) + .contained() + .with_style(style.container) + .constrained() + .with_height(theme.contact_finder.row_height) + .into_any() + } +} diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 5caebb9f0c..338ba6cf27 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,5 +1,8 @@ use crate::{ - contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, + branch_list::{build_branch_list, BranchList}, + contact_notification::ContactNotification, + contacts_popover, + face_pile::FacePile, toggle_deafen, toggle_mute, toggle_screen_sharing, LeaveCall, ToggleDeafen, ToggleMute, ToggleScreenSharing, }; @@ -12,12 +15,14 @@ use gpui::{ actions, color::Color, elements::*, + fonts::TextStyle, geometry::{rect::RectF, vector::vec2f, PathBuilder}, json::{self, ToJson}, platform::{CursorStyle, MouseButton}, AppContext, Entity, ImageData, LayoutContext, ModelHandle, SceneBuilder, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; +use picker::PickerEvent; use project::{Project, RepositoryEntry}; use std::{ops::Range, sync::Arc}; use theme::{AvatarStyle, Theme}; @@ -31,6 +36,8 @@ actions!( [ ToggleContactsMenu, ToggleUserMenu, + ToggleVcsMenu, + SwitchBranch, ShareProject, UnshareProject, ] @@ -41,6 +48,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(CollabTitlebarItem::share_project); cx.add_action(CollabTitlebarItem::unshare_project); cx.add_action(CollabTitlebarItem::toggle_user_menu); + cx.add_action(CollabTitlebarItem::toggle_vcs_menu); } pub struct CollabTitlebarItem { @@ -49,6 +57,7 @@ pub struct CollabTitlebarItem { client: Arc, workspace: WeakViewHandle, contacts_popover: Option>, + branch_popover: Option>, user_menu: ViewHandle, _subscriptions: Vec, } @@ -69,12 +78,11 @@ impl View for CollabTitlebarItem { return Empty::new().into_any(); }; - let project = self.project.read(cx); let theme = theme::current(cx).clone(); let mut left_container = Flex::row(); let mut right_container = Flex::row().align_children_center(); - left_container.add_child(self.collect_title_root_names(&project, theme.clone(), cx)); + left_container.add_child(self.collect_title_root_names(theme.clone(), cx)); let user = self.user_store.read(cx).current_user(); let peer_id = self.client.peer_id(); @@ -182,22 +190,28 @@ impl CollabTitlebarItem { menu.set_position_mode(OverlayPositionMode::Local); menu }), + branch_popover: None, _subscriptions: subscriptions, } } fn collect_title_root_names( &self, - project: &Project, theme: Arc, - cx: &ViewContext, + cx: &mut ViewContext, ) -> AnyElement { - let mut names_and_branches = project.visible_worktrees(cx).map(|worktree| { - let worktree = worktree.read(cx); - (worktree.root_name(), worktree.root_git_entry()) - }); + let project = self.project.read(cx); - let (name, entry) = names_and_branches.next().unwrap_or(("", None)); + let (name, entry) = { + let mut names_and_branches = project.visible_worktrees(cx).map(|worktree| { + let worktree = worktree.read(cx); + (worktree.root_name(), worktree.root_git_entry()) + }); + + names_and_branches.next().unwrap_or(("", None)) + }; + + let name = name.to_owned(); let branch_prepended = entry .as_ref() .and_then(RepositoryEntry::branch) @@ -212,22 +226,37 @@ impl CollabTitlebarItem { text: text_style, highlight_text: Some(highlight), }; + let highlights = (0..name.len()).into_iter().collect(); let mut ret = Flex::row().with_child( - Label::new(name.to_owned(), style.clone()) - .with_highlights((0..name.len()).into_iter().collect()) - .contained() - .aligned() - .left() - .into_any_named("title-project-name"), + Stack::new().with_child( + Label::new(name, style.clone()) + .with_highlights(highlights) + .contained() + .aligned() + .left() + .into_any_named("title-project-name"), + ), ); if let Some(git_branch) = branch_prepended { ret = ret.with_child( - Label::new(git_branch, style) - .contained() - .with_margin_right(item_spacing) - .aligned() - .left() - .into_any_named("title-project-branch"), + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + Label::new(git_branch, style) + .contained() + .with_margin_right(item_spacing) + .aligned() + .left() + .into_any_named("title-project-branch") + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_vcs_menu(&Default::default(), cx) + }), + ) + .with_children( + self.render_branches_popover_host(&theme.workspace.titlebar, cx), + ), ) } ret.into_any() @@ -320,7 +349,55 @@ impl CollabTitlebarItem { user_menu.toggle(Default::default(), AnchorCorner::TopRight, items, cx); }); } + fn render_branches_popover_host<'a>( + &'a self, + _theme: &'a theme::Titlebar, + cx: &'a mut ViewContext, + ) -> Option> { + self.branch_popover.as_ref().map(|child| { + let theme = theme::current(cx).clone(); + let child = ChildView::new(child, cx); + let child = MouseEventHandler::::new(0, cx, |_, _| { + Flex::column() + .with_child(child.flex(1., true)) + .contained() + .with_style(theme.contacts_popover.container) + .constrained() + .with_width(theme.contacts_popover.width) + .with_height(theme.contacts_popover.height) + }) + .on_click(MouseButton::Left, |_, _, _| {}) + .on_down_out(MouseButton::Left, move |_, _, cx| cx.emit(())) + .into_any(); + Overlay::new(child) + .with_fit_mode(OverlayFitMode::SwitchAnchor) + .with_anchor_corner(AnchorCorner::TopLeft) + .with_z_index(999) + .aligned() + .bottom() + .left() + .into_any() + }) + } + pub fn toggle_vcs_menu(&mut self, _: &ToggleVcsMenu, cx: &mut ViewContext) { + if self.branch_popover.take().is_none() { + let view = cx.add_view(|cx| build_branch_list(self.project.clone(), cx)); + cx.subscribe(&view, |this, _, event, cx| { + match event { + PickerEvent::Dismiss => { + this.contacts_popover = None; + } + } + + cx.notify(); + }) + .detach(); + self.branch_popover = Some(view); + } + + cx.notify(); + } fn render_toggle_contacts_button( &self, theme: &Theme, @@ -733,7 +810,7 @@ impl CollabTitlebarItem { self.contacts_popover.as_ref().map(|popover| { Overlay::new(ChildView::new(popover, cx)) .with_fit_mode(OverlayFitMode::SwitchAnchor) - .with_anchor_corner(AnchorCorner::TopRight) + .with_anchor_corner(AnchorCorner::TopLeft) .with_z_index(999) .aligned() .bottom() diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index a809b9c7e6..26d9c70a43 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,3 +1,4 @@ +mod branch_list; mod collab_titlebar_item; mod contact_finder; mod contact_list; @@ -28,6 +29,7 @@ actions!( ); pub fn init(app_state: &Arc, cx: &mut AppContext) { + branch_list::init(cx); collab_titlebar_item::init(cx); contact_list::init(cx); contact_finder::init(cx); diff --git a/crates/fs/src/repository.rs b/crates/fs/src/repository.rs index 488262887f..af6198eda4 100644 --- a/crates/fs/src/repository.rs +++ b/crates/fs/src/repository.rs @@ -1,6 +1,6 @@ use anyhow::Result; use collections::HashMap; -use git2::ErrorCode; +use git2::{BranchType, ErrorCode}; use parking_lot::Mutex; use rpc::proto; use serde_derive::{Deserialize, Serialize}; @@ -27,6 +27,12 @@ pub trait GitRepository: Send { fn statuses(&self) -> Option>; fn status(&self, path: &RepoPath) -> Result>; + fn branches(&self) -> Result> { + Ok(vec![]) + } + fn change_branch(&self, _: &str) -> Result<()> { + Ok(()) + } } impl std::fmt::Debug for dyn GitRepository { @@ -106,6 +112,30 @@ impl GitRepository for LibGitRepository { } } } + fn branches(&self) -> Result> { + let local_branches = self.branches(Some(BranchType::Local))?; + let valid_branches = local_branches + .filter_map(|branch| { + branch + .ok() + .map(|(branch, _)| branch.name().ok().flatten().map(String::from)) + .flatten() + }) + .collect(); + Ok(valid_branches) + } + fn change_branch(&self, name: &str) -> Result<()> { + let revision = self.find_branch(name, BranchType::Local)?; + let revision = revision.get(); + let as_tree = revision.peel_to_tree()?; + self.checkout_tree(as_tree.as_object(), None)?; + self.set_head( + revision + .name() + .ok_or_else(|| anyhow::anyhow!("Branch name could not be retrieved"))?, + )?; + Ok(()) + } } fn read_status(status: git2::Status) -> Option { From ac6e9c88e9400da223e2d0996c11c40d58ee24cd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 12:22:44 +0200 Subject: [PATCH 02/35] Render header and footer of git menu --- crates/collab_ui/src/branch_list.rs | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 8d755e8aa4..d946b9da3d 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -22,6 +22,7 @@ pub fn build_branch_list( matches: vec![], project, selected_index: 0, + last_query: String::default(), }, cx, ) @@ -33,6 +34,7 @@ pub struct BranchListDelegate { matches: Vec, project: ModelHandle, selected_index: usize, + last_query: String, } impl PickerDelegate for BranchListDelegate { @@ -109,6 +111,7 @@ impl PickerDelegate for BranchListDelegate { let delegate = picker.delegate_mut(); //delegate.branches = actions; delegate.matches = matches; + delegate.last_query = query; if delegate.matches.is_empty() { delegate.selected_index = 0; } else { @@ -175,4 +178,34 @@ impl PickerDelegate for BranchListDelegate { .with_height(theme.contact_finder.row_height) .into_any() } + fn render_header(&self, cx: &AppContext) -> Option>> { + let theme = &theme::current(cx); + let style = theme.picker.no_matches.label.clone(); + if self.last_query.is_empty() { + Some( + Flex::row() + .with_child(Label::new("Recent branches", style)) + .into_any(), + ) + } else { + Some( + Flex::row() + .with_child(Label::new("Branches", style)) + .into_any(), + ) + } + } + fn render_footer(&self, cx: &AppContext) -> Option>> { + if !self.last_query.is_empty() && !self.matches.is_empty() { + let theme = &theme::current(cx); + let style = theme.picker.no_matches.label.clone(); + Some( + Flex::row() + .with_child(Label::new(format!("{} matches", self.matches.len()), style)) + .into_any(), + ) + } else { + None + } + } } From 6747acbb84e9787fa530ba015fd4f17a4f085877 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 12:40:53 +0200 Subject: [PATCH 03/35] Trail off branch names --- crates/collab_ui/src/branch_list.rs | 25 +++++++++++++------- crates/collab_ui/src/collab_titlebar_item.rs | 1 - 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index d946b9da3d..ddd884523a 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -1,4 +1,3 @@ -use client::{ContactRequestStatus, User, UserStore}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext}; use picker::{Picker, PickerDelegate, PickerEvent}; @@ -124,10 +123,8 @@ impl PickerDelegate for BranchListDelegate { } fn confirm(&mut self, cx: &mut ViewContext>) { - log::error!("confirm {}", self.selected_index()); let current_pick = self.selected_index(); let current_pick = self.matches[current_pick].string.clone(); - log::error!("Hi? {current_pick}"); let project = self.project.read(cx); let mut cwd = project .visible_worktrees(cx) @@ -139,7 +136,6 @@ impl PickerDelegate for BranchListDelegate { .path .to_path_buf(); cwd.push(".git"); - log::error!("{current_pick}"); project .fs() .open_repo(&cwd) @@ -161,13 +157,21 @@ impl PickerDelegate for BranchListDelegate { selected: bool, cx: &gpui::AppContext, ) -> AnyElement> { + const DISPLAYED_MATCH_LEN: usize = 29; let theme = &theme::current(cx); - let user = &self.matches[ix]; + let hit = &self.matches[ix]; + let shortened_branch_name = util::truncate_and_trailoff(&hit.string, DISPLAYED_MATCH_LEN); + let highlights = hit + .positions + .iter() + .copied() + .filter(|index| index < &DISPLAYED_MATCH_LEN) + .collect(); let style = theme.picker.item.in_state(selected).style_for(mouse_state); Flex::row() .with_child( - Label::new(user.string.clone(), style.label.clone()) - .with_highlights(user.positions.clone()) + Label::new(shortened_branch_name.clone(), style.label.clone()) + .with_highlights(highlights) .contained() .aligned() .left(), @@ -199,9 +203,14 @@ impl PickerDelegate for BranchListDelegate { if !self.last_query.is_empty() && !self.matches.is_empty() { let theme = &theme::current(cx); let style = theme.picker.no_matches.label.clone(); + // Render "1 match" and "0 matches", "42 matches"etc. + let suffix = if self.matches.len() == 1 { "" } else { "es" }; Some( Flex::row() - .with_child(Label::new(format!("{} matches", self.matches.len()), style)) + .with_child(Label::new( + format!("{} match{}", self.matches.len(), suffix), + style, + )) .into_any(), ) } else { diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 338ba6cf27..e306aa20b3 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -361,7 +361,6 @@ impl CollabTitlebarItem { Flex::column() .with_child(child.flex(1., true)) .contained() - .with_style(theme.contacts_popover.container) .constrained() .with_width(theme.contacts_popover.width) .with_height(theme.contacts_popover.height) From 3027e4729abf2d9c83fe0d06683fa9834a4180f4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 13:28:50 +0200 Subject: [PATCH 04/35] Add timestamps to branches --- Cargo.lock | 1 + crates/collab_ui/src/branch_list.rs | 6 ++---- crates/fs/Cargo.toml | 1 + crates/fs/src/repository.rs | 21 +++++++++++++++------ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9b2e29ea0..a59332520b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2549,6 +2549,7 @@ dependencies = [ "smol", "sum_tree", "tempfile", + "time 0.3.21", "util", ] diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index ddd884523a..7da984a599 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -17,7 +17,6 @@ pub fn build_branch_list( ) -> BranchList { Picker::new( BranchListDelegate { - branches: vec!["Foo".into(), "bar/baz".into()], matches: vec![], project, selected_index: 0, @@ -29,7 +28,6 @@ pub fn build_branch_list( } pub struct BranchListDelegate { - branches: Vec, matches: Vec, project: ModelHandle, selected_index: usize, @@ -77,8 +75,8 @@ impl PickerDelegate for BranchListDelegate { .enumerate() .map(|(ix, command)| StringMatchCandidate { id: ix, - string: command.clone(), - char_bag: command.chars().collect(), + char_bag: command.name.chars().collect(), + string: command.name.into(), }) .collect::>() }) diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index cb738f567c..b3ebd224b0 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -31,6 +31,7 @@ serde_derive.workspace = true serde_json.workspace = true log.workspace = true libc = "0.2" +time.workspace = true [dev-dependencies] gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/fs/src/repository.rs b/crates/fs/src/repository.rs index af6198eda4..36df0dfb98 100644 --- a/crates/fs/src/repository.rs +++ b/crates/fs/src/repository.rs @@ -16,6 +16,12 @@ use util::ResultExt; pub use git2::Repository as LibGitRepository; +#[derive(Clone, Debug, Hash, PartialEq)] +pub struct Branch { + pub name: Box, + /// Timestamp of most recent commit, normalized to Unix Epoch format. + pub unix_timestamp: Option, +} #[async_trait::async_trait] pub trait GitRepository: Send { fn reload_index(&self); @@ -27,7 +33,7 @@ pub trait GitRepository: Send { fn statuses(&self) -> Option>; fn status(&self, path: &RepoPath) -> Result>; - fn branches(&self) -> Result> { + fn branches(&self) -> Result> { Ok(vec![]) } fn change_branch(&self, _: &str) -> Result<()> { @@ -112,14 +118,17 @@ impl GitRepository for LibGitRepository { } } } - fn branches(&self) -> Result> { + fn branches(&self) -> Result> { let local_branches = self.branches(Some(BranchType::Local))?; let valid_branches = local_branches .filter_map(|branch| { - branch - .ok() - .map(|(branch, _)| branch.name().ok().flatten().map(String::from)) - .flatten() + branch.ok().and_then(|(branch, _)| { + let name = branch.name().ok().flatten().map(Box::from)?; + Some(Branch { + name, + unix_timestamp: None, + }) + }) }) .collect(); Ok(valid_branches) From 54fad5969f5f0aa649cccb27493e48d7cf99f5b3 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:20:36 +0200 Subject: [PATCH 05/35] List recent branches --- crates/collab_ui/src/branch_list.rs | 23 ++++++++++++++++++----- crates/fs/src/repository.rs | 9 ++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 7da984a599..146e4c8041 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -2,7 +2,7 @@ use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext}; use picker::{Picker, PickerDelegate, PickerEvent}; use project::Project; -use std::sync::Arc; +use std::{cmp::Ordering, sync::Arc}; use util::{ResultExt, TryFutureExt}; pub fn init(cx: &mut AppContext) { @@ -67,9 +67,23 @@ impl PickerDelegate for BranchListDelegate { .path .to_path_buf(); cwd.push(".git"); - let branches = project.fs().open_repo(&cwd).unwrap().lock().branches(); - branches + let mut branches = project + .fs() + .open_repo(&cwd) .unwrap() + .lock() + .branches() + .unwrap(); + if query.is_empty() { + const RECENT_BRANCHES_COUNT: usize = 10; + // Do a partial sort to show recent-ish branches first. + branches.select_nth_unstable_by(RECENT_BRANCHES_COUNT, |lhs, rhs| { + rhs.unix_timestamp.cmp(&lhs.unix_timestamp) + }); + branches.truncate(RECENT_BRANCHES_COUNT); + branches.sort_unstable_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + } + branches .iter() .cloned() .enumerate() @@ -106,15 +120,14 @@ impl PickerDelegate for BranchListDelegate { picker .update(&mut cx, |picker, _| { let delegate = picker.delegate_mut(); - //delegate.branches = actions; delegate.matches = matches; - delegate.last_query = query; if delegate.matches.is_empty() { delegate.selected_index = 0; } else { delegate.selected_index = core::cmp::min(delegate.selected_index, delegate.matches.len() - 1); } + delegate.last_query = query; }) .log_err(); }) diff --git a/crates/fs/src/repository.rs b/crates/fs/src/repository.rs index 36df0dfb98..0e5fd8343f 100644 --- a/crates/fs/src/repository.rs +++ b/crates/fs/src/repository.rs @@ -124,9 +124,16 @@ impl GitRepository for LibGitRepository { .filter_map(|branch| { branch.ok().and_then(|(branch, _)| { let name = branch.name().ok().flatten().map(Box::from)?; + let timestamp = branch.get().peel_to_commit().ok()?.time(); + let unix_timestamp = timestamp.seconds(); + let timezone_offset = timestamp.offset_minutes(); + let utc_offset = + time::UtcOffset::from_whole_seconds(timezone_offset * 60).ok()?; + let unix_timestamp = + time::OffsetDateTime::from_unix_timestamp(unix_timestamp).ok()?; Some(Branch { name, - unix_timestamp: None, + unix_timestamp: Some(unix_timestamp.to_offset(utc_offset).unix_timestamp()), }) }) }) From c84f3b3bfc2a2434017ab23e3bcaeae98424623f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 20:12:20 +0200 Subject: [PATCH 06/35] Add toast for git checkout failure --- crates/collab_ui/src/branch_list.rs | 31 ++++++++++++++------ crates/collab_ui/src/collab_titlebar_item.rs | 22 +++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 146e4c8041..2558b24ae8 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -1,9 +1,10 @@ use fuzzy::{StringMatch, StringMatchCandidate}; -use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext}; +use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, ViewHandle}; use picker::{Picker, PickerDelegate, PickerEvent}; use project::Project; use std::{cmp::Ordering, sync::Arc}; use util::{ResultExt, TryFutureExt}; +use workspace::{Toast, Workspace}; pub fn init(cx: &mut AppContext) { Picker::::init(cx); @@ -12,13 +13,13 @@ pub fn init(cx: &mut AppContext) { pub type BranchList = Picker; pub fn build_branch_list( - project: ModelHandle, + workspace: ViewHandle, cx: &mut ViewContext, ) -> BranchList { Picker::new( BranchListDelegate { matches: vec![], - project, + workspace, selected_index: 0, last_query: String::default(), }, @@ -29,7 +30,7 @@ pub fn build_branch_list( pub struct BranchListDelegate { matches: Vec, - project: ModelHandle, + workspace: ViewHandle, selected_index: usize, last_query: String, } @@ -56,7 +57,7 @@ impl PickerDelegate for BranchListDelegate { let candidates = picker .read_with(&mut cx, |view, cx| { let delegate = view.delegate(); - let project = delegate.project.read(&cx); + let project = delegate.workspace.read(cx).project().read(&cx); let mut cwd = project .visible_worktrees(cx) .next() @@ -136,7 +137,7 @@ impl PickerDelegate for BranchListDelegate { fn confirm(&mut self, cx: &mut ViewContext>) { let current_pick = self.selected_index(); let current_pick = self.matches[current_pick].string.clone(); - let project = self.project.read(cx); + let project = self.workspace.read(cx).project().read(cx); let mut cwd = project .visible_worktrees(cx) .next() @@ -147,13 +148,25 @@ impl PickerDelegate for BranchListDelegate { .path .to_path_buf(); cwd.push(".git"); - project + let status = project .fs() .open_repo(&cwd) .unwrap() .lock() - .change_branch(¤t_pick) - .log_err(); + .change_branch(¤t_pick); + if let Err(err) = &status { + const GIT_CHECKOUT_FAILURE_ID: usize = 2048; + self.workspace.update(cx, |model, ctx| { + model.show_toast( + Toast::new( + GIT_CHECKOUT_FAILURE_ID, + format!("Failed to check out branch `{current_pick}`, error: `{err}`"), + ), + ctx, + ) + }); + } + status.log_err(); cx.emit(PickerEvent::Dismiss); } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index e306aa20b3..b38ac39798 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -381,18 +381,20 @@ impl CollabTitlebarItem { } pub fn toggle_vcs_menu(&mut self, _: &ToggleVcsMenu, cx: &mut ViewContext) { if self.branch_popover.take().is_none() { - let view = cx.add_view(|cx| build_branch_list(self.project.clone(), cx)); - cx.subscribe(&view, |this, _, event, cx| { - match event { - PickerEvent::Dismiss => { - this.contacts_popover = None; + if let Some(workspace) = self.workspace.upgrade(cx) { + let view = cx.add_view(|cx| build_branch_list(workspace, cx)); + cx.subscribe(&view, |this, _, event, cx| { + match event { + PickerEvent::Dismiss => { + this.contacts_popover = None; + } } - } - cx.notify(); - }) - .detach(); - self.branch_popover = Some(view); + cx.notify(); + }) + .detach(); + self.branch_popover = Some(view); + } } cx.notify(); From aeafa6f6d6242dd81c0162e6528b240f3c4b61c2 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:40:10 +0200 Subject: [PATCH 07/35] Fix build after rebase --- crates/collab_ui/src/collab_titlebar_item.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index b38ac39798..48a4e12ff5 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -254,9 +254,7 @@ impl CollabTitlebarItem { this.toggle_vcs_menu(&Default::default(), cx) }), ) - .with_children( - self.render_branches_popover_host(&theme.workspace.titlebar, cx), - ), + .with_children(self.render_branches_popover_host(&theme.titlebar, cx)), ) } ret.into_any() From e57364ede6d4e3f89a560934ed814e9ec93138a0 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:42:20 +0200 Subject: [PATCH 08/35] Remove unnecessary imports --- crates/collab_ui/src/branch_list.rs | 7 +++---- crates/collab_ui/src/collab_titlebar_item.rs | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 2558b24ae8..2fb8721c94 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -1,9 +1,8 @@ use fuzzy::{StringMatch, StringMatchCandidate}; -use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, ViewHandle}; +use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle}; use picker::{Picker, PickerDelegate, PickerEvent}; -use project::Project; -use std::{cmp::Ordering, sync::Arc}; -use util::{ResultExt, TryFutureExt}; +use std::sync::Arc; +use util::ResultExt; use workspace::{Toast, Workspace}; pub fn init(cx: &mut AppContext) { diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 48a4e12ff5..e6a0c98265 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -15,7 +15,6 @@ use gpui::{ actions, color::Color, elements::*, - fonts::TextStyle, geometry::{rect::RectF, vector::vec2f, PathBuilder}, json::{self, ToJson}, platform::{CursorStyle, MouseButton}, @@ -241,7 +240,7 @@ impl CollabTitlebarItem { ret = ret.with_child( Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |state, _| { + MouseEventHandler::::new(0, cx, |_, _| { Label::new(git_branch, style) .contained() .with_margin_right(item_spacing) From 98f71a7fa3822840665c5fe7a1e25909b12d98ed Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:47:50 +0200 Subject: [PATCH 09/35] Trail off project/branch name --- crates/collab_ui/src/collab_titlebar_item.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index e6a0c98265..5bef89d1ee 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -28,7 +28,8 @@ use theme::{AvatarStyle, Theme}; use util::ResultExt; use workspace::{FollowNextCollaborator, Workspace}; -// const MAX_TITLE_LENGTH: usize = 75; +const MAX_PROJECT_NAME_LENGTH: usize = 40; +const MAX_BRANCH_NAME_LENGTH: usize = 40; actions!( collab, @@ -210,11 +211,16 @@ impl CollabTitlebarItem { names_and_branches.next().unwrap_or(("", None)) }; - let name = name.to_owned(); + let name = util::truncate_and_trailoff(name, MAX_PROJECT_NAME_LENGTH); let branch_prepended = entry .as_ref() .and_then(RepositoryEntry::branch) - .map(|branch| format!("/{branch}")); + .map(|branch| { + format!( + "/{}", + util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH) + ) + }); let text_style = theme.titlebar.title.clone(); let item_spacing = theme.titlebar.item_spacing; From 1eb0f3d09156f01c280793d080702e3c0b78a7fb Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 12:12:35 +0200 Subject: [PATCH 10/35] Update toast for checkout failure --- crates/collab_ui/src/branch_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 2fb8721c94..eb6b9c6f54 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -159,7 +159,7 @@ impl PickerDelegate for BranchListDelegate { model.show_toast( Toast::new( GIT_CHECKOUT_FAILURE_ID, - format!("Failed to check out branch `{current_pick}`, error: `{err}`"), + format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"), ), ctx, ) From d000ea97398d64613056a4dba9f6602257bb1e36 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 12:57:49 +0200 Subject: [PATCH 11/35] Fix warning about unused variable --- crates/collab_ui/src/branch_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index eb6b9c6f54..2d014cc1b9 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -153,7 +153,7 @@ impl PickerDelegate for BranchListDelegate { .unwrap() .lock() .change_branch(¤t_pick); - if let Err(err) = &status { + if status.is_err() { const GIT_CHECKOUT_FAILURE_ID: usize = 2048; self.workspace.update(cx, |model, ctx| { model.show_toast( From 888d3b3fd620722a44483222cd4c3201153b15a4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:03:01 +0200 Subject: [PATCH 12/35] Project dropdown menu --- Cargo.lock | 2 + crates/collab_ui/Cargo.toml | 2 + crates/collab_ui/src/collab_titlebar_item.rs | 79 +++++++++++++++++-- crates/recent_projects/Cargo.toml | 1 + crates/recent_projects/src/recent_projects.rs | 21 ++++- 5 files changed, 95 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a59332520b..d16248c85a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1415,6 +1415,7 @@ dependencies = [ "picker", "postage", "project", + "recent_projects", "serde", "serde_derive", "settings", @@ -5376,6 +5377,7 @@ version = "0.1.0" dependencies = [ "db", "editor", + "futures 0.3.28", "fuzzy", "gpui", "language", diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index ee410ccba7..f81885c07a 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -35,6 +35,7 @@ gpui = { path = "../gpui" } menu = { path = "../menu" } picker = { path = "../picker" } project = { path = "../project" } +recent_projects = {path = "../recent_projects"} settings = { path = "../settings" } theme = { path = "../theme" } theme_selector = { path = "../theme_selector" } @@ -42,6 +43,7 @@ util = { path = "../util" } workspace = { path = "../workspace" } zed-actions = {path = "../zed-actions"} + anyhow.workspace = true futures.workspace = true log.workspace = true diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 5bef89d1ee..8dccf2a71a 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -23,6 +23,7 @@ use gpui::{ }; use picker::PickerEvent; use project::{Project, RepositoryEntry}; +use recent_projects::{build_recent_projects, RecentProjects}; use std::{ops::Range, sync::Arc}; use theme::{AvatarStyle, Theme}; use util::ResultExt; @@ -37,6 +38,7 @@ actions!( ToggleContactsMenu, ToggleUserMenu, ToggleVcsMenu, + ToggleProjectMenu, SwitchBranch, ShareProject, UnshareProject, @@ -49,6 +51,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(CollabTitlebarItem::unshare_project); cx.add_action(CollabTitlebarItem::toggle_user_menu); cx.add_action(CollabTitlebarItem::toggle_vcs_menu); + cx.add_action(CollabTitlebarItem::toggle_project_menu); } pub struct CollabTitlebarItem { @@ -58,6 +61,7 @@ pub struct CollabTitlebarItem { workspace: WeakViewHandle, contacts_popover: Option>, branch_popover: Option>, + project_popover: Option>, user_menu: ViewHandle, _subscriptions: Vec, } @@ -191,6 +195,7 @@ impl CollabTitlebarItem { menu }), branch_popover: None, + project_popover: None, _subscriptions: subscriptions, } } @@ -233,14 +238,22 @@ impl CollabTitlebarItem { }; let highlights = (0..name.len()).into_iter().collect(); let mut ret = Flex::row().with_child( - Stack::new().with_child( - Label::new(name, style.clone()) - .with_highlights(highlights) - .contained() - .aligned() - .left() - .into_any_named("title-project-name"), - ), + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |_, _| { + Label::new(name, style.clone()) + .with_highlights(highlights) + .contained() + .aligned() + .left() + .into_any_named("title-project-name") + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_project_menu(&Default::default(), cx) + }), + ) + .with_children(self.render_project_popover_host(&theme.titlebar, cx)), ); if let Some(git_branch) = branch_prepended { ret = ret.with_child( @@ -382,6 +395,36 @@ impl CollabTitlebarItem { .into_any() }) } + fn render_project_popover_host<'a>( + &'a self, + _theme: &'a theme::Titlebar, + cx: &'a mut ViewContext, + ) -> Option> { + self.project_popover.as_ref().map(|child| { + let theme = theme::current(cx).clone(); + let child = ChildView::new(child, cx); + let child = MouseEventHandler::::new(0, cx, |_, _| { + Flex::column() + .with_child(child.flex(1., true)) + .contained() + .constrained() + .with_width(theme.contacts_popover.width) + .with_height(theme.contacts_popover.height) + }) + .on_click(MouseButton::Left, |_, _, _| {}) + .on_down_out(MouseButton::Left, move |_, _, cx| cx.emit(())) + .into_any(); + + Overlay::new(child) + .with_fit_mode(OverlayFitMode::SwitchAnchor) + .with_anchor_corner(AnchorCorner::TopLeft) + .with_z_index(999) + .aligned() + .bottom() + .left() + .into_any() + }) + } pub fn toggle_vcs_menu(&mut self, _: &ToggleVcsMenu, cx: &mut ViewContext) { if self.branch_popover.take().is_none() { if let Some(workspace) = self.workspace.upgrade(cx) { @@ -402,6 +445,26 @@ impl CollabTitlebarItem { cx.notify(); } + + pub fn toggle_project_menu(&mut self, _: &ToggleProjectMenu, cx: &mut ViewContext) { + log::error!("Toggling project menu"); + if self.project_popover.take().is_none() { + let view = cx.add_view(|cx| build_recent_projects(self.workspace.clone(), cx)); + cx.subscribe(&view, |this, _, event, cx| { + match event { + PickerEvent::Dismiss => { + this.project_popover = None; + } + } + + cx.notify(); + }) + .detach(); + self.project_popover = Some(view); + } + + cx.notify(); + } fn render_toggle_contacts_button( &self, theme: &Theme, diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index 14f8853c9c..51774e8feb 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -21,6 +21,7 @@ util = { path = "../util"} theme = { path = "../theme" } workspace = { path = "../workspace" } +futures.workspace = true ordered-float.workspace = true postage.workspace = true smol.workspace = true diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index b13f72da0b..ed901a7da8 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -64,9 +64,26 @@ fn toggle( })) } -type RecentProjects = Picker; +pub fn build_recent_projects( + workspace: WeakViewHandle, + cx: &mut ViewContext, +) -> RecentProjects { + let workspaces = futures::executor::block_on(async { + WORKSPACE_DB + .recent_workspaces_on_disk() + .await + .unwrap_or_default() + .into_iter() + .map(|(_, location)| location) + .collect() + }); + Picker::new(RecentProjectsDelegate::new(workspace, workspaces), cx) + .with_theme(|theme| theme.picker.clone()) +} -struct RecentProjectsDelegate { +pub type RecentProjects = Picker; + +pub struct RecentProjectsDelegate { workspace: WeakViewHandle, workspace_locations: Vec, selected_match_index: usize, From 818ddbc7031f764a444e103b77eecd4eb2aba91b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:04:58 +0200 Subject: [PATCH 13/35] Make project dropdown exclusive wrt git menu --- crates/collab_ui/src/collab_titlebar_item.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 8dccf2a71a..c78ce2f4e1 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -439,6 +439,7 @@ impl CollabTitlebarItem { cx.notify(); }) .detach(); + self.project_popover.take(); self.branch_popover = Some(view); } } @@ -447,7 +448,6 @@ impl CollabTitlebarItem { } pub fn toggle_project_menu(&mut self, _: &ToggleProjectMenu, cx: &mut ViewContext) { - log::error!("Toggling project menu"); if self.project_popover.take().is_none() { let view = cx.add_view(|cx| build_recent_projects(self.workspace.clone(), cx)); cx.subscribe(&view, |this, _, event, cx| { @@ -460,6 +460,7 @@ impl CollabTitlebarItem { cx.notify(); }) .detach(); + self.branch_popover.take(); self.project_popover = Some(view); } From 081e340d26285692785871fe3c754153463d54e7 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:07:51 +0200 Subject: [PATCH 14/35] Do not query db on foreground thread. Co-authored-by: Mikayla --- crates/collab_ui/src/collab_titlebar_item.rs | 42 +++++++++++++------ crates/recent_projects/src/recent_projects.rs | 10 +---- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c78ce2f4e1..511113a360 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -27,7 +27,7 @@ use recent_projects::{build_recent_projects, RecentProjects}; use std::{ops::Range, sync::Arc}; use theme::{AvatarStyle, Theme}; use util::ResultExt; -use workspace::{FollowNextCollaborator, Workspace}; +use workspace::{FollowNextCollaborator, Workspace, WORKSPACE_DB}; const MAX_PROJECT_NAME_LENGTH: usize = 40; const MAX_BRANCH_NAME_LENGTH: usize = 40; @@ -448,23 +448,39 @@ impl CollabTitlebarItem { } pub fn toggle_project_menu(&mut self, _: &ToggleProjectMenu, cx: &mut ViewContext) { + let workspace = self.workspace.clone(); if self.project_popover.take().is_none() { - let view = cx.add_view(|cx| build_recent_projects(self.workspace.clone(), cx)); - cx.subscribe(&view, |this, _, event, cx| { - match event { - PickerEvent::Dismiss => { - this.project_popover = None; - } - } + cx.spawn(|this, mut cx| async move { + let workspaces = WORKSPACE_DB + .recent_workspaces_on_disk() + .await + .unwrap_or_default() + .into_iter() + .map(|(_, location)| location) + .collect(); - cx.notify(); + let workspace = workspace.clone(); + this.update(&mut cx, move |this, cx| { + let view = cx.add_view(|cx| build_recent_projects(workspace, workspaces, cx)); + + cx.subscribe(&view, |this, _, event, cx| { + match event { + PickerEvent::Dismiss => { + this.project_popover = None; + } + } + + cx.notify(); + }) + .detach(); + this.branch_popover.take(); + this.project_popover = Some(view); + cx.notify(); + }) + .log_err(); }) .detach(); - self.branch_popover.take(); - self.project_popover = Some(view); } - - cx.notify(); } fn render_toggle_contacts_button( &self, diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index ed901a7da8..b3c9655645 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -66,17 +66,9 @@ fn toggle( pub fn build_recent_projects( workspace: WeakViewHandle, + workspaces: Vec, cx: &mut ViewContext, ) -> RecentProjects { - let workspaces = futures::executor::block_on(async { - WORKSPACE_DB - .recent_workspaces_on_disk() - .await - .unwrap_or_default() - .into_iter() - .map(|(_, location)| location) - .collect() - }); Picker::new(RecentProjectsDelegate::new(workspace, workspaces), cx) .with_theme(|theme| theme.picker.clone()) } From c1a629215269b255b75629866fa79e229dd037fc Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 12:15:47 +0200 Subject: [PATCH 15/35] Add missing call to cx.notify --- crates/collab_ui/src/collab_titlebar_item.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 511113a360..1fdce6f186 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -481,6 +481,7 @@ impl CollabTitlebarItem { }) .detach(); } + cx.notify(); } fn render_toggle_contacts_button( &self, From 3be8977ee86a3fb86ff27fbeb9c39e4797e28c9f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:00:37 +0200 Subject: [PATCH 16/35] Switch branches within spawn() --- crates/collab_ui/src/branch_list.rs | 87 +++++++++++++++-------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 2d014cc1b9..038180b671 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -1,3 +1,4 @@ +use anyhow::{anyhow, bail}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle}; use picker::{Picker, PickerDelegate, PickerEvent}; @@ -53,7 +54,7 @@ impl PickerDelegate for BranchListDelegate { fn update_matches(&mut self, query: String, cx: &mut ViewContext>) -> Task<()> { cx.spawn(move |picker, mut cx| async move { - let candidates = picker + let Some(candidates) = picker .read_with(&mut cx, |view, cx| { let delegate = view.delegate(); let project = delegate.workspace.read(cx).project().read(&cx); @@ -67,13 +68,10 @@ impl PickerDelegate for BranchListDelegate { .path .to_path_buf(); cwd.push(".git"); - let mut branches = project - .fs() - .open_repo(&cwd) - .unwrap() + let Some(repo) = project.fs().open_repo(&cwd) else {bail!("Project does not have associated git repository.")}; + let mut branches = repo .lock() - .branches() - .unwrap(); + .branches()?; if query.is_empty() { const RECENT_BRANCHES_COUNT: usize = 10; // Do a partial sort to show recent-ish branches first. @@ -83,7 +81,7 @@ impl PickerDelegate for BranchListDelegate { branches.truncate(RECENT_BRANCHES_COUNT); branches.sort_unstable_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); } - branches + Ok(branches .iter() .cloned() .enumerate() @@ -92,9 +90,10 @@ impl PickerDelegate for BranchListDelegate { char_bag: command.name.chars().collect(), string: command.name.into(), }) - .collect::>() + .collect::>()) }) - .unwrap(); + .log_err() else { return; }; + let Some(candidates) = candidates.log_err() else {return;}; let matches = if query.is_empty() { candidates .into_iter() @@ -136,37 +135,43 @@ impl PickerDelegate for BranchListDelegate { fn confirm(&mut self, cx: &mut ViewContext>) { let current_pick = self.selected_index(); let current_pick = self.matches[current_pick].string.clone(); - let project = self.workspace.read(cx).project().read(cx); - let mut cwd = project - .visible_worktrees(cx) - .next() - .unwrap() - .read(cx) - .root_entry() - .unwrap() - .path - .to_path_buf(); - cwd.push(".git"); - let status = project - .fs() - .open_repo(&cwd) - .unwrap() - .lock() - .change_branch(¤t_pick); - if status.is_err() { - const GIT_CHECKOUT_FAILURE_ID: usize = 2048; - self.workspace.update(cx, |model, ctx| { - model.show_toast( - Toast::new( - GIT_CHECKOUT_FAILURE_ID, - format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"), - ), - ctx, - ) - }); - } - status.log_err(); - cx.emit(PickerEvent::Dismiss); + cx.spawn(|picker, mut cx| async move { + picker.update(&mut cx, |this, cx| { + let project = this.delegate().workspace.read(cx).project().read(cx); + let mut cwd = project + .visible_worktrees(cx) + .next() + .ok_or_else(|| anyhow!("There are no visisible worktrees."))? + .read(cx) + .root_entry() + .ok_or_else(|| anyhow!("Worktree has no root entry."))? + .path + .to_path_buf(); + cwd.push(".git"); + let status = project + .fs() + .open_repo(&cwd) + .ok_or_else(|| anyhow!("Could not open repository at path `{}`", cwd.as_os_str().to_string_lossy()))? + .lock() + .change_branch(¤t_pick); + if status.is_err() { + const GIT_CHECKOUT_FAILURE_ID: usize = 2048; + this.delegate().workspace.update(cx, |model, ctx| { + model.show_toast( + Toast::new( + GIT_CHECKOUT_FAILURE_ID, + format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"), + ), + ctx, + ) + }); + status?; + } + cx.emit(PickerEvent::Dismiss); + + Ok::<(), anyhow::Error>(()) + }).log_err(); + }).detach(); } fn dismissed(&mut self, cx: &mut ViewContext>) { From b699e5c142f900ae0e38574ec2ad1e11e1a693cf Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:23:27 +0200 Subject: [PATCH 17/35] Add styles to git menu --- crates/collab_ui/src/branch_list.rs | 40 +++++++++++-------------- crates/theme/src/theme.rs | 2 ++ styles/src/style_tree/contact_finder.ts | 2 ++ styles/src/style_tree/picker.ts | 29 ++++++++++++++++++ 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 038180b671..95e418e67f 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, bail}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle}; use picker::{Picker, PickerDelegate, PickerEvent}; -use std::sync::Arc; +use std::{ops::Not, sync::Arc}; use util::ResultExt; use workspace::{Toast, Workspace}; @@ -212,7 +212,7 @@ impl PickerDelegate for BranchListDelegate { } fn render_header(&self, cx: &AppContext) -> Option>> { let theme = &theme::current(cx); - let style = theme.picker.no_matches.label.clone(); + let style = theme.picker.header.clone(); if self.last_query.is_empty() { Some( Flex::row() @@ -221,28 +221,24 @@ impl PickerDelegate for BranchListDelegate { ) } else { Some( - Flex::row() - .with_child(Label::new("Branches", style)) + Stack::new() + .with_child( + Flex::row() + .with_child(Label::new("Branches", style.clone()).aligned().left()), + ) + .with_children(self.matches.is_empty().not().then(|| { + let suffix = if self.matches.len() == 1 { "" } else { "es" }; + Flex::row() + .align_children_center() + .with_child(Label::new( + format!("{} match{}", self.matches.len(), suffix), + style, + )) + .aligned() + .right() + })) .into_any(), ) } } - fn render_footer(&self, cx: &AppContext) -> Option>> { - if !self.last_query.is_empty() && !self.matches.is_empty() { - let theme = &theme::current(cx); - let style = theme.picker.no_matches.label.clone(); - // Render "1 match" and "0 matches", "42 matches"etc. - let suffix = if self.matches.len() == 1 { "" } else { "es" }; - Some( - Flex::row() - .with_child(Label::new( - format!("{} match{}", self.matches.len(), suffix), - style, - )) - .into_any(), - ) - } else { - None - } - } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index e54dcdfd1e..687547a306 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -585,6 +585,8 @@ pub struct Picker { pub empty_input_editor: FieldEditor, pub no_matches: ContainedLabel, pub item: Toggleable>, + pub header: LabelStyle, + pub footer: LabelStyle, } #[derive(Clone, Debug, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/contact_finder.ts b/styles/src/style_tree/contact_finder.ts index e61d100264..4815a4e98e 100644 --- a/styles/src/style_tree/contact_finder.ts +++ b/styles/src/style_tree/contact_finder.ts @@ -44,6 +44,8 @@ export default function contact_finder(theme: ColorScheme): any { no_matches: picker_style.no_matches, input_editor: picker_input, empty_input_editor: picker_input, + header: picker_style.header, + footer: picker_style.footer, }, row_height: 28, contact_avatar: { diff --git a/styles/src/style_tree/picker.ts b/styles/src/style_tree/picker.ts index 7ca76cd85f..9918d1a2d6 100644 --- a/styles/src/style_tree/picker.ts +++ b/styles/src/style_tree/picker.ts @@ -108,5 +108,34 @@ export default function picker(theme: ColorScheme): any { top: 8, }, }, + header: { + text: text(theme.lowest, "sans", "variant", { size: "xs" }), + padding: { + bottom: 4, + left: 12, + right: 12, + top: 4, + }, + margin: { + top: 1, + left: 4, + right: 4, + }, + }, + footer: { + text: text(theme.lowest, "sans", "variant", { size: "xs" }), + padding: { + bottom: 4, + left: 12, + right: 12, + top: 4, + }, + margin: { + top: 1, + left: 4, + right: 4, + }, + + } } } From ed75c316405814d2d06c96affb0d07de292ec3ad Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:38:38 +0200 Subject: [PATCH 18/35] Improve styling of git menu --- crates/collab_ui/src/branch_list.rs | 23 +++++++++++++++++++---- crates/theme/src/theme.rs | 4 ++-- styles/src/style_tree/picker.ts | 21 +++++---------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 95e418e67f..0abe83f766 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -215,8 +215,16 @@ impl PickerDelegate for BranchListDelegate { let style = theme.picker.header.clone(); if self.last_query.is_empty() { Some( - Flex::row() - .with_child(Label::new("Recent branches", style)) + Stack::new() + .with_child( + Flex::row() + .with_child(Label::new("Recent branches", style.label.clone())) + .contained() + .with_style(style.container) + .into_any(), + ) + .contained() + .with_style(style.container) .into_any(), ) } else { @@ -224,7 +232,11 @@ impl PickerDelegate for BranchListDelegate { Stack::new() .with_child( Flex::row() - .with_child(Label::new("Branches", style.clone()).aligned().left()), + .with_child( + Label::new("Branches", style.label.clone()).aligned().left(), + ) + .contained() + .with_style(style.container), ) .with_children(self.matches.is_empty().not().then(|| { let suffix = if self.matches.len() == 1 { "" } else { "es" }; @@ -232,11 +244,14 @@ impl PickerDelegate for BranchListDelegate { .align_children_center() .with_child(Label::new( format!("{} match{}", self.matches.len(), suffix), - style, + style.label, )) .aligned() .right() })) + .contained() + .with_style(style.container) + .constrained() .into_any(), ) } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 687547a306..c224131d1e 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -585,8 +585,8 @@ pub struct Picker { pub empty_input_editor: FieldEditor, pub no_matches: ContainedLabel, pub item: Toggleable>, - pub header: LabelStyle, - pub footer: LabelStyle, + pub header: ContainedLabel, + pub footer: ContainedLabel, } #[derive(Clone, Debug, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/picker.ts b/styles/src/style_tree/picker.ts index 9918d1a2d6..6a7221efb0 100644 --- a/styles/src/style_tree/picker.ts +++ b/styles/src/style_tree/picker.ts @@ -110,30 +110,19 @@ export default function picker(theme: ColorScheme): any { }, header: { text: text(theme.lowest, "sans", "variant", { size: "xs" }), - padding: { - bottom: 4, - left: 12, - right: 12, - top: 4, - }, + margin: { top: 1, - left: 4, - right: 4, + left: 8, + right: 8, }, }, footer: { text: text(theme.lowest, "sans", "variant", { size: "xs" }), - padding: { - bottom: 4, - left: 12, - right: 12, - top: 4, - }, margin: { top: 1, - left: 4, - right: 4, + left: 8, + right: 8, }, } From a5d9a10d7bf423b44bf43064b1a2dbb57ab235ea Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 19:48:28 +0200 Subject: [PATCH 19/35] Focus dropdowns on open --- crates/collab_ui/src/collab_titlebar_item.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 1fdce6f186..b68095878d 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -440,6 +440,7 @@ impl CollabTitlebarItem { }) .detach(); self.project_popover.take(); + cx.focus(&view); self.branch_popover = Some(view); } } @@ -473,6 +474,7 @@ impl CollabTitlebarItem { cx.notify(); }) .detach(); + cx.focus(&view); this.branch_popover.take(); this.project_popover = Some(view); cx.notify(); From cec884b5a52f076029b1e7851531ebce593ae555 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 20:07:44 +0200 Subject: [PATCH 20/35] Add styles for project name/git menu --- crates/collab_ui/src/collab_titlebar_item.rs | 67 ++++++++++---------- styles/src/style_tree/titlebar.ts | 5 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index b68095878d..0857b2b79a 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -220,29 +220,17 @@ impl CollabTitlebarItem { let branch_prepended = entry .as_ref() .and_then(RepositoryEntry::branch) - .map(|branch| { - format!( - "/{}", - util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH) - ) - }); - let text_style = theme.titlebar.title.clone(); + .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH)); + let project_style = theme.titlebar.title.clone(); + let git_style = theme.titlebar.git_branch.clone(); + let divider_style = theme.titlebar.project_name_divider.clone(); let item_spacing = theme.titlebar.item_spacing; - let mut highlight = text_style.clone(); - highlight.color = theme.titlebar.highlight_color; - - let style = LabelStyle { - text: text_style, - highlight_text: Some(highlight), - }; - let highlights = (0..name.len()).into_iter().collect(); let mut ret = Flex::row().with_child( Stack::new() .with_child( MouseEventHandler::::new(0, cx, |_, _| { - Label::new(name, style.clone()) - .with_highlights(highlights) + Label::new(name, project_style.text.clone()) .contained() .aligned() .left() @@ -251,28 +239,43 @@ impl CollabTitlebarItem { .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, this, cx| { this.toggle_project_menu(&Default::default(), cx) - }), + }) + .contained() + .with_style(project_style.container), ) .with_children(self.render_project_popover_host(&theme.titlebar, cx)), ); if let Some(git_branch) = branch_prepended { ret = ret.with_child( - Stack::new() + Flex::row() .with_child( - MouseEventHandler::::new(0, cx, |_, _| { - Label::new(git_branch, style) - .contained() - .with_margin_right(item_spacing) - .aligned() - .left() - .into_any_named("title-project-branch") - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_vcs_menu(&Default::default(), cx) - }), + Label::new("/", divider_style.text) + .contained() + .with_style(divider_style.container) + .aligned() + .left(), ) - .with_children(self.render_branches_popover_host(&theme.titlebar, cx)), + .with_child( + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |_, _| { + Label::new(git_branch, git_style.text) + .contained() + .with_margin_right(item_spacing) + .aligned() + .left() + .into_any_named("title-project-branch") + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click( + MouseButton::Left, + move |_, this, cx| { + this.toggle_vcs_menu(&Default::default(), cx) + }, + ), + ) + .with_children(self.render_branches_popover_host(&theme.titlebar, cx)), + ), ) } ret.into_any() diff --git a/styles/src/style_tree/titlebar.ts b/styles/src/style_tree/titlebar.ts index 067d619bb5..686f814a5d 100644 --- a/styles/src/style_tree/titlebar.ts +++ b/styles/src/style_tree/titlebar.ts @@ -173,8 +173,9 @@ export function titlebar(theme: ColorScheme): any { }, // Project - title: text(theme.lowest, "sans", "variant"), - highlight_color: text(theme.lowest, "sans", "active").color, + title: text(theme.lowest, "sans", "active"), + project_name_divider: text(theme.lowest, "sans", "variant"), + git_branch: text(theme.lowest, "sans", "variant"), // Collaborators leader_avatar: { From 7c2c1a279b2b78f9703707339781cd6ecc6f0bca Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 30 Jun 2023 20:09:30 +0200 Subject: [PATCH 21/35] Add missing rust-side definitions --- crates/theme/src/theme.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index c224131d1e..a4fbcedd32 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -118,8 +118,9 @@ pub struct Titlebar { #[serde(flatten)] pub container: ContainerStyle, pub height: f32, - pub title: TextStyle, - pub highlight_color: Color, + pub title: ContainedText, + pub project_name_divider: ContainedText, + pub git_branch: ContainedText, pub item_spacing: f32, pub face_pile_spacing: f32, pub avatar_ribbon: AvatarRibbon, From 525521eeb39ccb97f3f8ecba72e0d3e5aeb66f01 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 1 Jul 2023 01:38:36 +0200 Subject: [PATCH 22/35] Render match count next to branch label --- crates/collab_ui/src/branch_list.rs | 93 ++++++++++++++++------------- crates/picker/src/picker.rs | 10 +++- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 0abe83f766..8c06326780 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -1,6 +1,8 @@ use anyhow::{anyhow, bail}; use fuzzy::{StringMatch, StringMatchCandidate}; -use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle}; +use gpui::{ + elements::*, platform::MouseButton, AppContext, MouseState, Task, ViewContext, ViewHandle, +}; use picker::{Picker, PickerDelegate, PickerEvent}; use std::{ops::Not, sync::Arc}; use util::ResultExt; @@ -210,50 +212,55 @@ impl PickerDelegate for BranchListDelegate { .with_height(theme.contact_finder.row_height) .into_any() } - fn render_header(&self, cx: &AppContext) -> Option>> { + fn render_header( + &self, + cx: &mut ViewContext>, + ) -> Option>> { let theme = &theme::current(cx); let style = theme.picker.header.clone(); - if self.last_query.is_empty() { - Some( - Stack::new() - .with_child( - Flex::row() - .with_child(Label::new("Recent branches", style.label.clone())) - .contained() - .with_style(style.container) - .into_any(), - ) - .contained() - .with_style(style.container) - .into_any(), - ) + let label = if self.last_query.is_empty() { + Stack::new() + .with_child( + Flex::row() + .with_child(Label::new("Recent branches", style.label.clone())) + .contained() + .with_style(style.container) + .into_any(), + ) + .contained() + .with_style(style.container) + .into_any() } else { - Some( - Stack::new() - .with_child( - Flex::row() - .with_child( - Label::new("Branches", style.label.clone()).aligned().left(), - ) - .contained() - .with_style(style.container), - ) - .with_children(self.matches.is_empty().not().then(|| { - let suffix = if self.matches.len() == 1 { "" } else { "es" }; - Flex::row() - .align_children_center() - .with_child(Label::new( - format!("{} match{}", self.matches.len(), suffix), - style.label, - )) - .aligned() - .right() - })) - .contained() - .with_style(style.container) - .constrained() - .into_any(), - ) - } + Stack::new() + .with_child( + Flex::row() + .with_child(Label::new("Branches", style.label.clone()).aligned().left()) + .contained() + .with_style(style.container), + ) + .with_children(self.matches.is_empty().not().then(|| { + let suffix = if self.matches.len() == 1 { "" } else { "es" }; + Flex::row() + .align_children_center() + .with_child(Label::new( + format!("{} match{}", self.matches.len(), suffix), + style.label, + )) + .aligned() + .right() + })) + .contained() + .with_style(style.container) + .constrained() + .into_any() + }; + Some( + MouseEventHandler::::new(0, cx, move |_, _| label) + .on_click(MouseButton::Left, move |_, _, _| {}) + .on_down_out(MouseButton::Left, move |_, _, cx| { + cx.emit(PickerEvent::Dismiss) + }) + .into_any(), + ) } } diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 33d6e84241..978d018ccf 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -45,10 +45,16 @@ pub trait PickerDelegate: Sized + 'static { fn center_selection_after_match_updates(&self) -> bool { false } - fn render_header(&self, _cx: &AppContext) -> Option>> { + fn render_header( + &self, + _cx: &mut ViewContext>, + ) -> Option>> { None } - fn render_footer(&self, _cx: &AppContext) -> Option>> { + fn render_footer( + &self, + _cx: &mut ViewContext>, + ) -> Option>> { None } } From 026ad191ebefce8234035295175e16ebe9263978 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 1 Jul 2023 01:49:00 +0200 Subject: [PATCH 23/35] Dismiss dropdowns on click out --- crates/collab_ui/src/collab_titlebar_item.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 0857b2b79a..ea90a879ce 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -385,7 +385,11 @@ impl CollabTitlebarItem { .with_height(theme.contacts_popover.height) }) .on_click(MouseButton::Left, |_, _, _| {}) - .on_down_out(MouseButton::Left, move |_, _, cx| cx.emit(())) + .on_down_out(MouseButton::Left, move |_, this, cx| { + this.branch_popover.take(); + cx.emit(()); + cx.notify(); + }) .into_any(); Overlay::new(child) @@ -415,7 +419,11 @@ impl CollabTitlebarItem { .with_height(theme.contacts_popover.height) }) .on_click(MouseButton::Left, |_, _, _| {}) - .on_down_out(MouseButton::Left, move |_, _, cx| cx.emit(())) + .on_down_out(MouseButton::Left, move |_, this, cx| { + this.project_popover.take(); + cx.emit(()); + cx.notify(); + }) .into_any(); Overlay::new(child) From 4eedc3e6464564ab536c7cd1470a6c3262ccf4f1 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 3 Jul 2023 16:15:41 +0200 Subject: [PATCH 24/35] Remove flex from underneath the pickers --- crates/collab_ui/src/collab_titlebar_item.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index ea90a879ce..1a4869a583 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -377,8 +377,8 @@ impl CollabTitlebarItem { let theme = theme::current(cx).clone(); let child = ChildView::new(child, cx); let child = MouseEventHandler::::new(0, cx, |_, _| { - Flex::column() - .with_child(child.flex(1., true)) + child + .flex(1., true) .contained() .constrained() .with_width(theme.contacts_popover.width) @@ -390,6 +390,7 @@ impl CollabTitlebarItem { cx.emit(()); cx.notify(); }) + .contained() .into_any(); Overlay::new(child) @@ -411,8 +412,8 @@ impl CollabTitlebarItem { let theme = theme::current(cx).clone(); let child = ChildView::new(child, cx); let child = MouseEventHandler::::new(0, cx, |_, _| { - Flex::column() - .with_child(child.flex(1., true)) + child + .flex(1., true) .contained() .constrained() .with_width(theme.contacts_popover.width) From 14eab4e94fae61b860bba70c8e74ba979bbf53c4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:22:43 +0200 Subject: [PATCH 25/35] branch list: dismiss correct window on PickerEvent. Query proper window --- crates/collab_ui/src/branch_list.rs | 18 ++++++++---------- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 8c06326780..e0f85aa65a 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -60,24 +60,24 @@ impl PickerDelegate for BranchListDelegate { .read_with(&mut cx, |view, cx| { let delegate = view.delegate(); let project = delegate.workspace.read(cx).project().read(&cx); - let mut cwd = project + let mut cwd = + project .visible_worktrees(cx) .next() .unwrap() .read(cx) - .root_entry() - .unwrap() - .path + .abs_path() .to_path_buf(); cwd.push(".git"); let Some(repo) = project.fs().open_repo(&cwd) else {bail!("Project does not have associated git repository.")}; let mut branches = repo .lock() .branches()?; - if query.is_empty() { - const RECENT_BRANCHES_COUNT: usize = 10; + const RECENT_BRANCHES_COUNT: usize = 10; + if query.is_empty() && branches.len() > RECENT_BRANCHES_COUNT { + // Truncate list of recent branches // Do a partial sort to show recent-ish branches first. - branches.select_nth_unstable_by(RECENT_BRANCHES_COUNT, |lhs, rhs| { + branches.select_nth_unstable_by(RECENT_BRANCHES_COUNT - 1, |lhs, rhs| { rhs.unix_timestamp.cmp(&lhs.unix_timestamp) }); branches.truncate(RECENT_BRANCHES_COUNT); @@ -145,9 +145,7 @@ impl PickerDelegate for BranchListDelegate { .next() .ok_or_else(|| anyhow!("There are no visisible worktrees."))? .read(cx) - .root_entry() - .ok_or_else(|| anyhow!("Worktree has no root entry."))? - .path + .abs_path() .to_path_buf(); cwd.push(".git"); let status = project diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 1a4869a583..8af3acf7be 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -444,7 +444,7 @@ impl CollabTitlebarItem { cx.subscribe(&view, |this, _, event, cx| { match event { PickerEvent::Dismiss => { - this.contacts_popover = None; + this.branch_popover = None; } } From e9b34de7c8189632da35430dfea8888d6e1cb5aa Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 4 Jul 2023 16:00:59 +0200 Subject: [PATCH 26/35] Fix click behaviour of vcs/project dropdowns --- crates/collab_ui/src/collab_titlebar_item.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 8af3acf7be..3bc890f91b 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -237,9 +237,10 @@ impl CollabTitlebarItem { .into_any_named("title-project-name") }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, this, cx| { + .on_down(MouseButton::Left, move |_, this, cx| { this.toggle_project_menu(&Default::default(), cx) }) + .on_click(MouseButton::Left, move |_, _, _| {}) .contained() .with_style(project_style.container), ) @@ -267,12 +268,10 @@ impl CollabTitlebarItem { .into_any_named("title-project-branch") }) .with_cursor_style(CursorStyle::PointingHand) - .on_click( - MouseButton::Left, - move |_, this, cx| { - this.toggle_vcs_menu(&Default::default(), cx) - }, - ), + .on_down(MouseButton::Left, move |_, this, cx| { + this.toggle_vcs_menu(&Default::default(), cx) + }) + .on_click(MouseButton::Left, move |_, _, _| {}), ) .with_children(self.render_branches_popover_host(&theme.titlebar, cx)), ), From 48371ab8b2ba823278dc5fb0853673128e3a71c5 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 4 Jul 2023 16:30:17 +0200 Subject: [PATCH 27/35] Remove PickerEvent::Dismiss emission from picker header --- crates/collab_ui/src/branch_list.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index e0f85aa65a..a1693fd6c4 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -255,9 +255,7 @@ impl PickerDelegate for BranchListDelegate { Some( MouseEventHandler::::new(0, cx, move |_, _| label) .on_click(MouseButton::Left, move |_, _, _| {}) - .on_down_out(MouseButton::Left, move |_, _, cx| { - cx.emit(PickerEvent::Dismiss) - }) + .on_down_out(MouseButton::Left, move |_, _, _| {}) .into_any(), ) } From 64b77bfa8d194cfddfc8e8b98db0abf6b711e73f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:04:16 +0200 Subject: [PATCH 28/35] Remove stacks from branch list header Co-authored-by: Antonio --- crates/collab_ui/src/branch_list.rs | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index a1693fd6c4..25999bf995 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -217,25 +217,13 @@ impl PickerDelegate for BranchListDelegate { let theme = &theme::current(cx); let style = theme.picker.header.clone(); let label = if self.last_query.is_empty() { - Stack::new() - .with_child( - Flex::row() - .with_child(Label::new("Recent branches", style.label.clone())) - .contained() - .with_style(style.container) - .into_any(), - ) + Flex::row() + .with_child(Label::new("Recent branches", style.label.clone())) .contained() .with_style(style.container) - .into_any() } else { - Stack::new() - .with_child( - Flex::row() - .with_child(Label::new("Branches", style.label.clone()).aligned().left()) - .contained() - .with_style(style.container), - ) + Flex::row() + .with_child(Label::new("Branches", style.label.clone()).aligned().left()) .with_children(self.matches.is_empty().not().then(|| { let suffix = if self.matches.len() == 1 { "" } else { "es" }; Flex::row() @@ -249,14 +237,7 @@ impl PickerDelegate for BranchListDelegate { })) .contained() .with_style(style.container) - .constrained() - .into_any() }; - Some( - MouseEventHandler::::new(0, cx, move |_, _| label) - .on_click(MouseButton::Left, move |_, _, _| {}) - .on_down_out(MouseButton::Left, move |_, _, _| {}) - .into_any(), - ) + Some(label.into_any()) } } From 8b3b1a607458a41fb8c2a9a9bb4d671da81ff97e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:08:21 +0200 Subject: [PATCH 29/35] fixup! Remove stacks from branch list header --- crates/collab_ui/src/branch_list.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 25999bf995..229d1fdc29 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -1,8 +1,6 @@ use anyhow::{anyhow, bail}; use fuzzy::{StringMatch, StringMatchCandidate}; -use gpui::{ - elements::*, platform::MouseButton, AppContext, MouseState, Task, ViewContext, ViewHandle, -}; +use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle}; use picker::{Picker, PickerDelegate, PickerEvent}; use std::{ops::Not, sync::Arc}; use util::ResultExt; From cc88bff1ff6d378429a61fa34bcd36caed05214e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:25:33 +0200 Subject: [PATCH 30/35] Fix click-through behaviour of git panel Co-authored-by: Antonio --- crates/gpui/src/app/window.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 23fbb33fe1..a04aaf8a28 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -482,20 +482,19 @@ impl<'a> WindowContext<'a> { // If there is already clicked_button stored, don't replace it. if self.window.clicked_button.is_none() { - self.window.clicked_region_ids = self - .window - .mouse_regions - .iter() - .filter_map(|(region, _)| { - if region.bounds.contains_point(e.position) { - Some(region.id()) - } else { - None - } - }) - .collect(); + let window = &mut *self.window; + window.clicked_region_ids.clear(); - self.window.clicked_button = Some(e.button); + let mut highest_z_index = 0; + for (region, z_index) in window.mouse_regions.iter() { + if region.bounds.contains_point(e.position) && *z_index >= highest_z_index { + highest_z_index = *z_index; + window.clicked_region_ids.clear(); + window.clicked_region_ids.insert(region.id()); + } + } + + window.clicked_button = Some(e.button); } mouse_events.push(MouseEvent::Down(MouseDown { From 85add260f6fbde3d412dc8afa6ce60e65ac77413 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:48:52 +0200 Subject: [PATCH 31/35] Track regions instead of clicks. Get rid of superfluous params in RenderParams related to hover & click state. Co-authored-by: Antonio --- crates/gpui/src/app.rs | 14 ++++++---- crates/gpui/src/app/window.rs | 49 ++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 20043a9093..dfd5fe7bb2 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3304,11 +3304,15 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { let region_id = MouseRegionId::new::(self.view_id, region_id); MouseState { hovered: self.window.hovered_region_ids.contains(®ion_id), - clicked: self - .window - .clicked_region_ids - .get(®ion_id) - .and_then(|_| self.window.clicked_button), + clicked: if let Some((clicked_region_id, button)) = self.window.clicked_region { + if region_id == clicked_region_id { + Some(button) + } else { + None + } + } else { + None + }, accessed_hovered: false, accessed_clicked: false, } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index a04aaf8a28..5a77017a11 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -53,7 +53,7 @@ pub struct Window { last_mouse_moved_event: Option, pub(crate) hovered_region_ids: HashSet, pub(crate) clicked_region_ids: HashSet, - pub(crate) clicked_button: Option, + pub(crate) clicked_region: Option<(MouseRegionId, MouseButton)>, mouse_position: Vector2F, text_layout_cache: TextLayoutCache, } @@ -86,7 +86,7 @@ impl Window { last_mouse_moved_event: None, hovered_region_ids: Default::default(), clicked_region_ids: Default::default(), - clicked_button: None, + clicked_region: None, mouse_position: vec2f(0., 0.), titlebar_height, appearance, @@ -480,21 +480,32 @@ impl<'a> WindowContext<'a> { // specific ancestor element that contained both [positions]' // So we need to store the overlapping regions on mouse down. - // If there is already clicked_button stored, don't replace it. - if self.window.clicked_button.is_none() { - let window = &mut *self.window; - window.clicked_region_ids.clear(); + // If there is already region being clicked, don't replace it. + if self.window.clicked_region.is_none() { + self.window.clicked_region_ids = self + .window + .mouse_regions + .iter() + .filter_map(|(region, _)| { + if region.bounds.contains_point(e.position) { + Some(region.id()) + } else { + None + } + }) + .collect(); let mut highest_z_index = 0; - for (region, z_index) in window.mouse_regions.iter() { + let mut clicked_region_id = None; + for (region, z_index) in self.window.mouse_regions.iter() { if region.bounds.contains_point(e.position) && *z_index >= highest_z_index { highest_z_index = *z_index; - window.clicked_region_ids.clear(); - window.clicked_region_ids.insert(region.id()); + clicked_region_id = Some(region.id()); } } - window.clicked_button = Some(e.button); + self.window.clicked_region = + clicked_region_id.map(|region_id| (region_id, e.button)); } mouse_events.push(MouseEvent::Down(MouseDown { @@ -559,7 +570,7 @@ impl<'a> WindowContext<'a> { prev_mouse_position: self.window.mouse_position, platform_event: e.clone(), })); - } else if let Some(clicked_button) = self.window.clicked_button { + } else if let Some((_, clicked_button)) = self.window.clicked_region { // Mouse up event happened outside the current window. Simulate mouse up button event let button_event = e.to_button_event(clicked_button); mouse_events.push(MouseEvent::Up(MouseUp { @@ -682,8 +693,8 @@ impl<'a> WindowContext<'a> { // Only raise click events if the released button is the same as the one stored if self .window - .clicked_button - .map(|clicked_button| clicked_button == e.button) + .clicked_region + .map(|(_, clicked_button)| clicked_button == e.button) .unwrap_or(false) { // Clear clicked regions and clicked button @@ -691,7 +702,7 @@ impl<'a> WindowContext<'a> { &mut self.window.clicked_region_ids, Default::default(), ); - self.window.clicked_button = None; + self.window.clicked_region = None; // Find regions which still overlap with the mouse since the last MouseDown happened for (mouse_region, _) in self.window.mouse_regions.iter().rev() { @@ -866,18 +877,10 @@ impl<'a> WindowContext<'a> { } for view_id in &invalidation.updated { let titlebar_height = self.window.titlebar_height; - let hovered_region_ids = self.window.hovered_region_ids.clone(); - let clicked_region_ids = self - .window - .clicked_button - .map(|button| (self.window.clicked_region_ids.clone(), button)); - let element = self .render_view(RenderParams { view_id: *view_id, titlebar_height, - hovered_region_ids, - clicked_region_ids, refreshing: false, appearance, }) @@ -1182,8 +1185,6 @@ impl<'a> WindowContext<'a> { pub struct RenderParams { pub view_id: usize, pub titlebar_height: f32, - pub hovered_region_ids: HashSet, - pub clicked_region_ids: Option<(HashSet, MouseButton)>, pub refreshing: bool, pub appearance: Appearance, } From ec47464bba1e77379a0f3ca7b8cb169a9300b50b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:56:08 +0200 Subject: [PATCH 32/35] branch_list: Show match count on the right hand side of a header. Co-authored-by: Antonio --- crates/collab_ui/src/branch_list.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/collab_ui/src/branch_list.rs b/crates/collab_ui/src/branch_list.rs index 229d1fdc29..16fefbd2eb 100644 --- a/crates/collab_ui/src/branch_list.rs +++ b/crates/collab_ui/src/branch_list.rs @@ -221,17 +221,14 @@ impl PickerDelegate for BranchListDelegate { .with_style(style.container) } else { Flex::row() - .with_child(Label::new("Branches", style.label.clone()).aligned().left()) + .with_child(Label::new("Branches", style.label.clone())) .with_children(self.matches.is_empty().not().then(|| { let suffix = if self.matches.len() == 1 { "" } else { "es" }; - Flex::row() - .align_children_center() - .with_child(Label::new( - format!("{} match{}", self.matches.len(), suffix), - style.label, - )) - .aligned() - .right() + Label::new( + format!("{} match{}", self.matches.len(), suffix), + style.label, + ) + .flex_float() })) .contained() .with_style(style.container) From 0e0d78df84f49b07f2b337985da235975c873943 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 18:04:40 +0200 Subject: [PATCH 33/35] Do not render recent paths in toolbar's project switcher --- crates/recent_projects/src/recent_projects.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index b3c9655645..4ba6103167 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -48,7 +48,7 @@ fn toggle( let workspace = cx.weak_handle(); cx.add_view(|cx| { RecentProjects::new( - RecentProjectsDelegate::new(workspace, workspace_locations), + RecentProjectsDelegate::new(workspace, workspace_locations, true), cx, ) .with_max_size(800., 1200.) @@ -69,8 +69,11 @@ pub fn build_recent_projects( workspaces: Vec, cx: &mut ViewContext, ) -> RecentProjects { - Picker::new(RecentProjectsDelegate::new(workspace, workspaces), cx) - .with_theme(|theme| theme.picker.clone()) + Picker::new( + RecentProjectsDelegate::new(workspace, workspaces, false), + cx, + ) + .with_theme(|theme| theme.picker.clone()) } pub type RecentProjects = Picker; @@ -80,18 +83,21 @@ pub struct RecentProjectsDelegate { workspace_locations: Vec, selected_match_index: usize, matches: Vec, + render_paths: bool, } impl RecentProjectsDelegate { fn new( workspace: WeakViewHandle, workspace_locations: Vec, + render_paths: bool, ) -> Self { Self { workspace, workspace_locations, selected_match_index: 0, matches: Default::default(), + render_paths, } } } @@ -197,6 +203,7 @@ impl PickerDelegate for RecentProjectsDelegate { highlighted_location .paths .into_iter() + .filter(|_| self.render_paths) .map(|highlighted_path| highlighted_path.render(style.label.clone())), ) .flex(1., false) From 1baa13561d374152a82c182da2b417e66a64280d Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 5 Jul 2023 12:50:56 -0400 Subject: [PATCH 34/35] Update project & git menus to be Toggleable> Co-Authored-By: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> --- crates/collab_ui/src/collab_titlebar_item.rs | 18 ++++++++++-------- crates/theme/src/theme.rs | 4 ++-- styles/src/style_tree/titlebar.ts | 9 +++++++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 3bc890f91b..c8f1d7cf30 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -221,17 +221,19 @@ impl CollabTitlebarItem { .as_ref() .and_then(RepositoryEntry::branch) .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH)); - let project_style = theme.titlebar.title.clone(); - let git_style = theme.titlebar.git_branch.clone(); + let project_style = theme.titlebar.project_menu_button.clone(); + let git_style = theme.titlebar.git_menu_button.clone(); let divider_style = theme.titlebar.project_name_divider.clone(); let item_spacing = theme.titlebar.item_spacing; let mut ret = Flex::row().with_child( Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |_, _| { - Label::new(name, project_style.text.clone()) + MouseEventHandler::::new(0, cx, |mouse_state, _| { + let style = project_style.in_state(self.project_popover.is_some()).style_for(mouse_state); + Label::new(name, style.text.clone()) .contained() + .with_style(style.container) .aligned() .left() .into_any_named("title-project-name") @@ -241,8 +243,6 @@ impl CollabTitlebarItem { this.toggle_project_menu(&Default::default(), cx) }) .on_click(MouseButton::Left, move |_, _, _| {}) - .contained() - .with_style(project_style.container), ) .with_children(self.render_project_popover_host(&theme.titlebar, cx)), ); @@ -259,9 +259,11 @@ impl CollabTitlebarItem { .with_child( Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |_, _| { - Label::new(git_branch, git_style.text) + MouseEventHandler::::new(0, cx, |mouse_state, _| { + let style = git_style.in_state(self.branch_popover.is_some()).style_for(mouse_state); + Label::new(git_branch, style.text.clone()) .contained() + .with_style(style.container.clone()) .with_margin_right(item_spacing) .aligned() .left() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index a4fbcedd32..3a7924f36e 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -118,9 +118,9 @@ pub struct Titlebar { #[serde(flatten)] pub container: ContainerStyle, pub height: f32, - pub title: ContainedText, + pub project_menu_button: Toggleable>, pub project_name_divider: ContainedText, - pub git_branch: ContainedText, + pub git_menu_button: Toggleable>, pub item_spacing: f32, pub face_pile_spacing: f32, pub avatar_ribbon: AvatarRibbon, diff --git a/styles/src/style_tree/titlebar.ts b/styles/src/style_tree/titlebar.ts index 686f814a5d..109db8db20 100644 --- a/styles/src/style_tree/titlebar.ts +++ b/styles/src/style_tree/titlebar.ts @@ -173,9 +173,14 @@ export function titlebar(theme: ColorScheme): any { }, // Project - title: text(theme.lowest, "sans", "active"), project_name_divider: text(theme.lowest, "sans", "variant"), - git_branch: text(theme.lowest, "sans", "variant"), + + project_menu_button: toggleable_text_button(theme, { + color: 'base', + }), + git_menu_button: toggleable_text_button(theme, { + color: 'variant', + }), // Collaborators leader_avatar: { From b80281e556577c03c72027a45078aeaa0e5435c9 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 5 Jul 2023 18:57:06 +0200 Subject: [PATCH 35/35] cargo fmt --- crates/collab_ui/src/collab_titlebar_item.rs | 32 ++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c8f1d7cf30..73450e7c7d 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -230,7 +230,9 @@ impl CollabTitlebarItem { Stack::new() .with_child( MouseEventHandler::::new(0, cx, |mouse_state, _| { - let style = project_style.in_state(self.project_popover.is_some()).style_for(mouse_state); + let style = project_style + .in_state(self.project_popover.is_some()) + .style_for(mouse_state); Label::new(name, style.text.clone()) .contained() .with_style(style.container) @@ -242,7 +244,7 @@ impl CollabTitlebarItem { .on_down(MouseButton::Left, move |_, this, cx| { this.toggle_project_menu(&Default::default(), cx) }) - .on_click(MouseButton::Left, move |_, _, _| {}) + .on_click(MouseButton::Left, move |_, _, _| {}), ) .with_children(self.render_project_popover_host(&theme.titlebar, cx)), ); @@ -259,16 +261,22 @@ impl CollabTitlebarItem { .with_child( Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |mouse_state, _| { - let style = git_style.in_state(self.branch_popover.is_some()).style_for(mouse_state); - Label::new(git_branch, style.text.clone()) - .contained() - .with_style(style.container.clone()) - .with_margin_right(item_spacing) - .aligned() - .left() - .into_any_named("title-project-branch") - }) + MouseEventHandler::::new( + 0, + cx, + |mouse_state, _| { + let style = git_style + .in_state(self.branch_popover.is_some()) + .style_for(mouse_state); + Label::new(git_branch, style.text.clone()) + .contained() + .with_style(style.container.clone()) + .with_margin_right(item_spacing) + .aligned() + .left() + .into_any_named("title-project-branch") + }, + ) .with_cursor_style(CursorStyle::PointingHand) .on_down(MouseButton::Left, move |_, this, cx| { this.toggle_vcs_menu(&Default::default(), cx)