1use context_menu::{ContextMenu, ContextMenuItem};
2use gpui::{
3 elements::*,
4 platform::{CursorStyle, MouseButton},
5 AnyElement, Element, Entity, View, ViewContext, ViewHandle, WeakViewHandle,
6};
7use settings::Settings;
8use workspace::Workspace;
9
10pub struct BranchesButton {
11 workspace: WeakViewHandle<Workspace>,
12 popup_menu: ViewHandle<ContextMenu>,
13}
14
15impl Entity for BranchesButton {
16 type Event = ();
17}
18
19impl View for BranchesButton {
20 fn ui_name() -> &'static str {
21 "BranchesButton"
22 }
23
24 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
25 let Some(workspace) = self.workspace.upgrade(cx) else {
26 return Empty::new().into_any();
27 };
28
29 let project = workspace.read(cx).project().read(cx);
30 let only_one_worktree = project.visible_worktrees(cx).count() == 1;
31 let branches_count: usize = project
32 .visible_worktrees(cx)
33 .map(|worktree_handle| worktree_handle.read(cx).snapshot().git_entries().count())
34 .sum();
35 let branch_caption: String = if only_one_worktree {
36 project
37 .visible_worktrees(cx)
38 .next()
39 .unwrap()
40 .read(cx)
41 .snapshot()
42 .root_git_entry()
43 .and_then(|entry| entry.branch())
44 .map(|branch| branch.to_string())
45 .unwrap_or_else(|| "".to_owned())
46 } else {
47 branches_count.to_string()
48 };
49 let is_popup_menu_visible = self.popup_menu.read(cx).visible();
50
51 let theme = cx.global::<Settings>().theme.clone();
52
53 Stack::new()
54 .with_child(
55 MouseEventHandler::<Self, _>::new(0, cx, {
56 let theme = theme.clone();
57 move |state, _cx| {
58 let style = theme
59 .workspace
60 .titlebar
61 .toggle_contacts_button
62 .style_for(state, is_popup_menu_visible);
63
64 Flex::row()
65 .with_child(
66 Svg::new("icons/version_control_branch_12.svg")
67 .with_color(style.color)
68 .constrained()
69 .with_width(style.icon_width)
70 .aligned()
71 // .constrained()
72 // .with_width(style.button_width)
73 // .with_height(style.button_width)
74 // .contained()
75 // .with_style(style.container)
76 .into_any_named("version-control-branch-icon"),
77 )
78 .with_child(
79 Label::new(branch_caption, theme.workspace.titlebar.title.clone())
80 .contained()
81 .with_style(style.container)
82 .aligned(),
83 )
84 .constrained()
85 .with_height(style.button_width)
86 .contained()
87 .with_style(style.container)
88 }
89 })
90 .with_cursor_style(CursorStyle::PointingHand)
91 .on_click(MouseButton::Left, move |_, this, cx| {
92 this.deploy_branches_menu(cx);
93 })
94 .with_tooltip::<Self>(
95 0,
96 "Branches".into(),
97 None,
98 theme.tooltip.clone(),
99 cx,
100 ),
101 )
102 .with_child(
103 ChildView::new(&self.popup_menu, cx)
104 .aligned()
105 .bottom()
106 .left(),
107 )
108 .into_any_named("branches-button")
109 }
110}
111
112impl BranchesButton {
113 pub fn new(workspace: ViewHandle<Workspace>, cx: &mut ViewContext<Self>) -> Self {
114 cx.observe(&workspace, |_, _, cx| cx.notify()).detach();
115 Self {
116 workspace: workspace.downgrade(),
117 popup_menu: cx.add_view(|cx| {
118 let mut menu = ContextMenu::new(cx);
119 menu.set_position_mode(OverlayPositionMode::Local);
120 menu
121 }),
122 }
123 }
124
125 pub fn deploy_branches_menu(&mut self, cx: &mut ViewContext<Self>) {
126 let mut menu_options = vec![];
127
128 if let Some(workspace) = self.workspace.upgrade(cx) {
129 let project = workspace.read(cx).project().read(cx);
130
131 let worktrees_with_branches = project
132 .visible_worktrees(cx)
133 .map(|worktree_handle| {
134 worktree_handle
135 .read(cx)
136 .snapshot()
137 .git_entries()
138 .filter_map(|entry| {
139 entry.branch().map(|branch| {
140 let repo_name = entry.work_directory();
141 if let Some(name) = repo_name.file_name() {
142 (name.to_string_lossy().to_string(), branch)
143 } else {
144 ("WORKTREE ROOT".into(), branch)
145 }
146 })
147 })
148 .collect::<Vec<_>>()
149 })
150 .flatten();
151
152 let context_menu_items = worktrees_with_branches.map(|(repo_name, branch_name)| {
153 let caption = format!("{} / {}", repo_name, branch_name);
154 ContextMenuItem::handler(caption.to_owned(), move |_| {
155 println!("{}", caption);
156 })
157 });
158 menu_options.extend(context_menu_items);
159 }
160
161 self.popup_menu.update(cx, |menu, cx| {
162 menu.show(Default::default(), AnchorCorner::TopLeft, menu_options, cx);
163 });
164 }
165}