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
 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}