invalid_item_view.rs

  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}