active_file_button.rs

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