tool_selector.rs

  1use std::sync::Arc;
  2
  3use assistant_tool::{ToolSource, ToolWorkingSet};
  4use gpui::Entity;
  5use scripting_tool::ScriptingTool;
  6use ui::{prelude::*, ContextMenu, IconButtonShape, PopoverMenu, Tooltip};
  7
  8pub struct ToolSelector {
  9    tools: Arc<ToolWorkingSet>,
 10}
 11
 12impl ToolSelector {
 13    pub fn new(tools: Arc<ToolWorkingSet>, _cx: &mut Context<Self>) -> Self {
 14        Self { tools }
 15    }
 16
 17    fn build_context_menu(
 18        &self,
 19        window: &mut Window,
 20        cx: &mut Context<Self>,
 21    ) -> Entity<ContextMenu> {
 22        ContextMenu::build(window, cx, |mut menu, _window, cx| {
 23            let tools_by_source = self.tools.tools_by_source(cx);
 24
 25            let all_tools_enabled = self.tools.are_all_tools_enabled();
 26            menu = menu.header("Tools").toggleable_entry(
 27                "All Tools",
 28                all_tools_enabled,
 29                IconPosition::End,
 30                None,
 31                {
 32                    let tools = self.tools.clone();
 33                    move |_window, cx| {
 34                        if all_tools_enabled {
 35                            tools.disable_all_tools(cx);
 36                        } else {
 37                            tools.enable_all_tools();
 38                        }
 39                    }
 40                },
 41            );
 42
 43            for (source, tools) in tools_by_source {
 44                let mut tools = tools
 45                    .into_iter()
 46                    .map(|tool| {
 47                        let source = tool.source();
 48                        let name = tool.name().into();
 49                        let is_enabled = self.tools.is_enabled(&source, &name);
 50
 51                        (source, name, is_enabled)
 52                    })
 53                    .collect::<Vec<_>>();
 54
 55                if ToolSource::Native == source {
 56                    tools.push((
 57                        ToolSource::Native,
 58                        ScriptingTool::NAME.into(),
 59                        self.tools.is_scripting_tool_enabled(),
 60                    ));
 61                    tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b));
 62                }
 63
 64                menu = match source {
 65                    ToolSource::Native => menu.header("Zed"),
 66                    ToolSource::ContextServer { id } => menu.separator().header(id),
 67                };
 68
 69                for (source, name, is_enabled) in tools {
 70                    menu =
 71                        menu.toggleable_entry(name.clone(), is_enabled, IconPosition::End, None, {
 72                            let tools = self.tools.clone();
 73                            move |_window, _cx| {
 74                                if name.as_ref() == ScriptingTool::NAME {
 75                                    if is_enabled {
 76                                        tools.disable_scripting_tool();
 77                                    } else {
 78                                        tools.enable_scripting_tool();
 79                                    }
 80                                } else {
 81                                    if is_enabled {
 82                                        tools.disable(source.clone(), &[name.clone()]);
 83                                    } else {
 84                                        tools.enable(source.clone(), &[name.clone()]);
 85                                    }
 86                                }
 87                            }
 88                        });
 89                }
 90            }
 91
 92            menu
 93        })
 94    }
 95}
 96
 97impl Render for ToolSelector {
 98    fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
 99        let this = cx.entity().clone();
100        PopoverMenu::new("tool-selector")
101            .menu(move |window, cx| {
102                Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
103            })
104            .trigger_with_tooltip(
105                IconButton::new("tool-selector-button", IconName::SettingsAlt)
106                    .shape(IconButtonShape::Square)
107                    .icon_size(IconSize::Small)
108                    .icon_color(Color::Muted),
109                Tooltip::text("Customize Tools"),
110            )
111            .anchor(gpui::Corner::BottomLeft)
112    }
113}