1use std::path::{Path, PathBuf};
8
9use gpui::App;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct PictureMenu {
15 url: String,
16 file: Option<PathBuf>,
17}
18
19#[derive(Clone, Copy)]
21enum Action {
22 Open,
23 Reveal,
24 CopyImage,
25 CopyAddress,
26}
27
28impl PictureMenu {
29 pub fn new(url: &str, base: Option<&Path>) -> Self {
32 let file = (!url.contains("://")).then(|| match base {
33 Some(base) => base.join(url),
34 None => PathBuf::from(url),
35 });
36 Self {
37 url: url.to_owned(),
38 file,
39 }
40 }
41
42 fn actions(&self) -> Vec<Option<Action>> {
45 let mut actions = Vec::new();
46 if self.file.is_some() {
47 actions.extend([Some(Action::Open), Some(Action::Reveal), None]);
48 if self.format().is_some() {
49 actions.push(Some(Action::CopyImage));
50 }
51 }
52 actions.push(Some(Action::CopyAddress));
53 actions
54 }
55
56 pub fn items(&self) -> Vec<ui::menu::Item> {
57 self.actions()
58 .into_iter()
59 .map(|action| match action {
60 None => ui::menu::Item::Separator,
61 Some(Action::Open) => ui::menu::Item::action("Open"),
62 Some(Action::Reveal) => ui::menu::Item::action(if cfg!(target_os = "macos") {
63 "Reveal in Finder"
64 } else {
65 "Show in Folder"
66 }),
67 Some(Action::CopyImage) => ui::menu::Item::action("Copy Image"),
68 Some(Action::CopyAddress) => ui::menu::Item::action("Copy Image Address"),
69 })
70 .collect()
71 }
72
73 fn format(&self) -> Option<gpui::ImageFormat> {
75 let extension = self.file.as_ref()?.extension()?.to_str()?.to_lowercase();
76 gpui::ImageFormat::from_mime_type(&format!("image/{extension}"))
77 }
78
79 pub fn run(&self, row: usize, cx: &mut App) {
81 let Some(Some(action)) = self.actions().get(row).copied() else {
82 return;
83 };
84 match (action, &self.file) {
85 (Action::Open, Some(file)) => cx.open_with_system(file),
86 (Action::Reveal, Some(file)) => cx.reveal_path(file),
87 (Action::CopyImage, Some(file)) => {
88 if let (Some(format), Ok(bytes)) = (self.format(), std::fs::read(file)) {
89 cx.write_to_clipboard(gpui::ClipboardItem::new_image(
90 &gpui::Image::from_bytes(format, bytes),
91 ));
92 }
93 }
94 (Action::CopyAddress, _) => {
95 cx.write_to_clipboard(gpui::ClipboardItem::new_string(self.url.clone()));
96 }
97 _ => {}
98 }
99 }
100}