project_index_button.rs

  1use assistant_tooling::ToolRegistry;
  2use gpui::{percentage, prelude::*, Animation, AnimationExt, Model, Transformation};
  3use semantic_index::{ProjectIndex, Status};
  4use std::{sync::Arc, time::Duration};
  5use ui::{prelude::*, ButtonLike, Color, Icon, IconName, Indicator, Tooltip};
  6
  7use crate::tools::ProjectIndexTool;
  8
  9pub struct ProjectIndexButton {
 10    project_index: Model<ProjectIndex>,
 11    tool_registry: Arc<ToolRegistry>,
 12}
 13
 14impl ProjectIndexButton {
 15    pub fn new(
 16        project_index: Model<ProjectIndex>,
 17        tool_registry: Arc<ToolRegistry>,
 18        cx: &mut ViewContext<Self>,
 19    ) -> Self {
 20        cx.subscribe(&project_index, |_this, _, _status: &Status, cx| {
 21            cx.notify();
 22        })
 23        .detach();
 24        Self {
 25            project_index,
 26            tool_registry,
 27        }
 28    }
 29
 30    pub fn set_enabled(&mut self, enabled: bool) {
 31        self.tool_registry
 32            .set_tool_enabled::<ProjectIndexTool>(enabled);
 33    }
 34}
 35
 36impl Render for ProjectIndexButton {
 37    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
 38        let status = self.project_index.read(cx).status();
 39        let is_enabled = self.tool_registry.is_tool_enabled::<ProjectIndexTool>();
 40
 41        let icon = if is_enabled {
 42            match status {
 43                Status::Idle => Icon::new(IconName::Code)
 44                    .size(IconSize::XSmall)
 45                    .color(Color::Default),
 46                Status::Loading => Icon::new(IconName::Code)
 47                    .size(IconSize::XSmall)
 48                    .color(Color::Muted),
 49                Status::Scanning { .. } => Icon::new(IconName::Code)
 50                    .size(IconSize::XSmall)
 51                    .color(Color::Muted),
 52            }
 53        } else {
 54            Icon::new(IconName::Code)
 55                .size(IconSize::XSmall)
 56                .color(Color::Disabled)
 57        };
 58
 59        let indicator = if is_enabled {
 60            match status {
 61                Status::Idle => Some(Indicator::dot().color(Color::Success)),
 62                Status::Scanning { .. } => Some(Indicator::dot().color(Color::Warning)),
 63                Status::Loading => Some(Indicator::icon(
 64                    Icon::new(IconName::Spinner)
 65                        .color(Color::Accent)
 66                        .with_animation(
 67                            "arrow-circle",
 68                            Animation::new(Duration::from_secs(2)).repeat(),
 69                            |icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
 70                        ),
 71                )),
 72            }
 73        } else {
 74            None
 75        };
 76
 77        ButtonLike::new("project-index")
 78            .child(
 79                ui::IconWithIndicator::new(icon, indicator)
 80                    .indicator_border_color(Some(gpui::transparent_black())),
 81            )
 82            .tooltip({
 83                move |cx| {
 84                    let (tooltip, meta) = match (is_enabled, status) {
 85                        (false, _) => (
 86                            "Project index disabled".to_string(),
 87                            Some("Click to enable".to_string()),
 88                        ),
 89                        (_, Status::Idle) => (
 90                            "Project index ready".to_string(),
 91                            Some("Click to disable".to_string()),
 92                        ),
 93                        (_, Status::Loading) => ("Project index loading...".to_string(), None),
 94                        (_, Status::Scanning { remaining_count }) => (
 95                            "Project index scanning...".to_string(),
 96                            Some(format!("{} remaining...", remaining_count)),
 97                        ),
 98                    };
 99
100                    if let Some(meta) = meta {
101                        Tooltip::with_meta(tooltip, None, meta, cx)
102                    } else {
103                        Tooltip::text(tooltip, cx)
104                    }
105                }
106            })
107            .on_click(cx.listener(move |this, _, cx| {
108                this.set_enabled(!is_enabled);
109                cx.notify();
110            }))
111    }
112}