1use std::{path::Path, sync::Arc};
2
3use gpui::{EventEmitter, FocusHandle, Focusable, div};
4use ui::{
5 App, Button, ButtonCommon, ButtonStyle, Clickable, Context, FluentBuilder, InteractiveElement,
6 KeyBinding, Label, LabelCommon, LabelSize, ParentElement, Render, SharedString, Styled as _,
7 Window, h_flex, v_flex,
8};
9use zed_actions::workspace::OpenWithSystem;
10
11use crate::Item;
12
13/// A view to display when a certain buffer/image/other item fails to open.
14pub struct InvalidItemView {
15 /// Which path was attempted to open.
16 pub abs_path: Arc<Path>,
17 /// An error message, happened when opening the item.
18 pub error: SharedString,
19 is_local: bool,
20 focus_handle: FocusHandle,
21}
22
23impl InvalidItemView {
24 pub fn new(
25 abs_path: &Path,
26 is_local: bool,
27 e: &anyhow::Error,
28 _: &mut Window,
29 cx: &mut App,
30 ) -> Self {
31 Self {
32 is_local,
33 abs_path: Arc::from(abs_path),
34 error: format!("{}", e.root_cause()).into(),
35 focus_handle: cx.focus_handle(),
36 }
37 }
38}
39
40impl Item for InvalidItemView {
41 type Event = ();
42
43 fn tab_content_text(&self, mut detail: usize, _: &App) -> SharedString {
44 // Ensure we always render at least the filename.
45 detail += 1;
46
47 let path = self.abs_path.as_ref();
48
49 let mut prefix = path;
50 while detail > 0 {
51 if let Some(parent) = prefix.parent() {
52 prefix = parent;
53 detail -= 1;
54 } else {
55 break;
56 }
57 }
58
59 let path = if detail > 0 {
60 path
61 } else {
62 path.strip_prefix(prefix).unwrap_or(path)
63 };
64
65 SharedString::new(path.to_string_lossy())
66 }
67}
68
69impl EventEmitter<()> for InvalidItemView {}
70
71impl Focusable for InvalidItemView {
72 fn focus_handle(&self, _: &App) -> FocusHandle {
73 self.focus_handle.clone()
74 }
75}
76
77impl Render for InvalidItemView {
78 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl gpui::IntoElement {
79 let abs_path = self.abs_path.clone();
80 let path0 = self.abs_path.clone();
81
82 v_flex()
83 .size_full()
84 .track_focus(&self.focus_handle(cx))
85 .flex_none()
86 .justify_center()
87 .overflow_hidden()
88 .key_context("InvalidItem")
89 .child(
90 h_flex().size_full().justify_center().child(
91 v_flex()
92 .justify_center()
93 .gap_2()
94 .child(h_flex().justify_center().child("Could not open file"))
95 .child(
96 h_flex().justify_center().child(
97 div()
98 .whitespace_normal()
99 .text_center()
100 .child(Label::new(self.error.clone()).size(LabelSize::Small)),
101 ),
102 )
103 .when(self.is_local, |contents| {
104 contents
105 .child(
106 h_flex().justify_center().child(
107 Button::new("open-with-system", "Open in Default App")
108 .on_click(move |_, _, cx| {
109 cx.open_with_system(&abs_path);
110 })
111 .style(ButtonStyle::Outlined)
112 .key_binding(KeyBinding::for_action(
113 &OpenWithSystem,
114 cx,
115 )),
116 ),
117 )
118 .child(
119 h_flex().justify_center().child(
120 Button::new(
121 "open-with-encoding",
122 "Try a Different Encoding",
123 )
124 .style(ButtonStyle::Outlined)
125 .on_click(
126 move |_, window, cx| {
127 window.dispatch_action(
128 Box::new(
129 zed_actions::encodings_ui::OpenWithEncoding(
130 path0.clone(),
131 ),
132 ),
133 cx,
134 )
135 },
136 ),
137 ),
138 )
139 }),
140 ),
141 )
142 }
143}