active_file_button.rs

  1use crate::attachments::ActiveEditorAttachmentTool;
  2use assistant_tooling::AttachmentRegistry;
  3use editor::Editor;
  4use gpui::{prelude::*, Subscription, View};
  5use std::sync::Arc;
  6use ui::{prelude::*, ButtonLike, Color, Icon, IconName, Tooltip};
  7use workspace::Workspace;
  8
  9#[derive(Clone)]
 10enum Status {
 11    ActiveFile(String),
 12    #[allow(dead_code)]
 13    NoFile,
 14}
 15
 16pub struct ActiveFileButton {
 17    attachment_registry: Arc<AttachmentRegistry>,
 18    status: Status,
 19    #[allow(dead_code)]
 20    workspace_subscription: Subscription,
 21}
 22
 23impl ActiveFileButton {
 24    pub fn new(
 25        attachment_registry: Arc<AttachmentRegistry>,
 26        workspace: View<Workspace>,
 27        cx: &mut ViewContext<Self>,
 28    ) -> Self {
 29        let workspace_subscription = cx.subscribe(&workspace, Self::handle_workspace_event);
 30
 31        cx.defer(move |this, cx| this.update_active_buffer(workspace.clone(), cx));
 32
 33        Self {
 34            attachment_registry,
 35            status: Status::NoFile,
 36            workspace_subscription,
 37        }
 38    }
 39
 40    pub fn set_enabled(&mut self, enabled: bool) {
 41        self.attachment_registry
 42            .set_attachment_tool_enabled::<ActiveEditorAttachmentTool>(enabled);
 43    }
 44
 45    pub fn update_active_buffer(&mut self, workspace: View<Workspace>, cx: &mut ViewContext<Self>) {
 46        let active_buffer = workspace
 47            .read(cx)
 48            .active_item(cx)
 49            .and_then(|item| Some(item.act_as::<Editor>(cx)?.read(cx).buffer().clone()));
 50
 51        if let Some(buffer) = active_buffer {
 52            let buffer = buffer.read(cx);
 53
 54            if let Some(singleton) = buffer.as_singleton() {
 55                let singleton = singleton.read(cx);
 56
 57                let filename: String = singleton
 58                    .file()
 59                    .map(|file| file.path().to_string_lossy())
 60                    .unwrap_or("Untitled".into())
 61                    .into();
 62
 63                self.status = Status::ActiveFile(filename);
 64            }
 65        }
 66    }
 67
 68    fn handle_workspace_event(
 69        &mut self,
 70        workspace: View<Workspace>,
 71        event: &workspace::Event,
 72        cx: &mut ViewContext<Self>,
 73    ) {
 74        if let workspace::Event::ActiveItemChanged = event {
 75            self.update_active_buffer(workspace, cx);
 76        }
 77    }
 78}
 79
 80impl Render for ActiveFileButton {
 81    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
 82        let is_enabled = self
 83            .attachment_registry
 84            .is_attachment_tool_enabled::<ActiveEditorAttachmentTool>();
 85
 86        let icon = if is_enabled {
 87            Icon::new(IconName::File)
 88                .size(IconSize::XSmall)
 89                .color(Color::Default)
 90        } else {
 91            Icon::new(IconName::File)
 92                .size(IconSize::XSmall)
 93                .color(Color::Disabled)
 94        };
 95
 96        let indicator = None;
 97
 98        let status = self.status.clone();
 99
100        ButtonLike::new("active-file-button")
101            .child(
102                ui::IconWithIndicator::new(icon, indicator)
103                    .indicator_border_color(Some(gpui::transparent_black())),
104            )
105            .tooltip({
106                move |cx| {
107                    let status = status.clone();
108                    let (tooltip, meta) = match (is_enabled, status) {
109                        (false, _) => (
110                            "Active file disabled".to_string(),
111                            Some("Click to enable".to_string()),
112                        ),
113                        (true, Status::ActiveFile(filename)) => (
114                            format!("Active file {filename} enabled"),
115                            Some("Click to disable".to_string()),
116                        ),
117                        (true, Status::NoFile) => {
118                            ("No file active for conversation".to_string(), None)
119                        }
120                    };
121
122                    if let Some(meta) = meta {
123                        Tooltip::with_meta(tooltip, None, meta, cx)
124                    } else {
125                        Tooltip::text(tooltip, cx)
126                    }
127                }
128            })
129            .on_click(cx.listener(move |this, _, cx| {
130                this.set_enabled(!is_enabled);
131                cx.notify();
132            }))
133    }
134}