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}