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