branches_button.rs

  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}