toolbar_controls.rs

  1use crate::{BufferDiagnosticsEditor, ProjectDiagnosticsEditor, ToggleDiagnosticsRefresh};
  2use gpui::{Context, EventEmitter, ParentElement, Render, Window};
  3use language::DiagnosticEntry;
  4use text::{Anchor, BufferId};
  5use ui::prelude::*;
  6use ui::{IconButton, IconButtonShape, IconName, Tooltip};
  7use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, item::ItemHandle};
  8
  9pub struct ToolbarControls {
 10    editor: Option<Box<dyn DiagnosticsToolbarEditor>>,
 11}
 12
 13pub(crate) trait DiagnosticsToolbarEditor: Send + Sync {
 14    /// Informs the toolbar whether warnings are included in the diagnostics.
 15    fn include_warnings(&self, cx: &App) -> bool;
 16    /// Toggles whether warning diagnostics should be displayed by the
 17    /// diagnostics editor.
 18    fn toggle_warnings(&self, window: &mut Window, cx: &mut App);
 19    /// Indicates whether any of the excerpts displayed by the diagnostics
 20    /// editor are stale.
 21    fn has_stale_excerpts(&self, cx: &App) -> bool;
 22    /// Indicates whether the diagnostics editor is currently updating the
 23    /// diagnostics.
 24    fn is_updating(&self, cx: &App) -> bool;
 25    /// Requests that the diagnostics editor stop updating the diagnostics.
 26    fn stop_updating(&self, cx: &mut App);
 27    /// Requests that the diagnostics editor updates the displayed diagnostics
 28    /// with the latest information.
 29    fn refresh_diagnostics(&self, window: &mut Window, cx: &mut App);
 30    /// Returns a list of diagnostics for the provided buffer id.
 31    fn get_diagnostics_for_buffer(
 32        &self,
 33        buffer_id: BufferId,
 34        cx: &App,
 35    ) -> Vec<DiagnosticEntry<Anchor>>;
 36}
 37
 38impl Render for ToolbarControls {
 39    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 40        let mut has_stale_excerpts = false;
 41        let mut include_warnings = false;
 42        let mut is_updating = false;
 43
 44        match &self.editor {
 45            Some(editor) => {
 46                include_warnings = editor.include_warnings(cx);
 47                has_stale_excerpts = editor.has_stale_excerpts(cx);
 48                is_updating = editor.is_updating(cx);
 49            }
 50            None => {}
 51        }
 52
 53        let warning_tooltip = if include_warnings {
 54            "Exclude Warnings"
 55        } else {
 56            "Include Warnings"
 57        };
 58
 59        let warning_color = if include_warnings {
 60            Color::Warning
 61        } else {
 62            Color::Muted
 63        };
 64
 65        h_flex()
 66            .gap_1()
 67            .map(|div| {
 68                if is_updating {
 69                    div.child(
 70                        IconButton::new("stop-updating", IconName::Stop)
 71                            .icon_color(Color::Info)
 72                            .shape(IconButtonShape::Square)
 73                            .tooltip(Tooltip::for_action_title(
 74                                "Stop diagnostics update",
 75                                &ToggleDiagnosticsRefresh,
 76                            ))
 77                            .on_click(cx.listener(move |toolbar_controls, _, _, cx| {
 78                                match toolbar_controls.editor() {
 79                                    Some(editor) => {
 80                                        editor.stop_updating(cx);
 81                                        cx.notify();
 82                                    }
 83                                    None => {}
 84                                }
 85                            })),
 86                    )
 87                } else {
 88                    div.child(
 89                        IconButton::new("refresh-diagnostics", IconName::ArrowCircle)
 90                            .icon_color(Color::Info)
 91                            .shape(IconButtonShape::Square)
 92                            .disabled(!has_stale_excerpts)
 93                            .tooltip(Tooltip::for_action_title(
 94                                "Refresh diagnostics",
 95                                &ToggleDiagnosticsRefresh,
 96                            ))
 97                            .on_click(cx.listener({
 98                                move |toolbar_controls, _, window, cx| match toolbar_controls
 99                                    .editor()
100                                {
101                                    Some(editor) => editor.refresh_diagnostics(window, cx),
102                                    None => {}
103                                }
104                            })),
105                    )
106                }
107            })
108            .child(
109                IconButton::new("toggle-warnings", IconName::Warning)
110                    .icon_color(warning_color)
111                    .shape(IconButtonShape::Square)
112                    .tooltip(Tooltip::text(warning_tooltip))
113                    .on_click(cx.listener(|this, _, window, cx| match &this.editor {
114                        Some(editor) => editor.toggle_warnings(window, cx),
115                        None => {}
116                    })),
117            )
118    }
119}
120
121impl EventEmitter<ToolbarItemEvent> for ToolbarControls {}
122
123impl ToolbarItemView for ToolbarControls {
124    fn set_active_pane_item(
125        &mut self,
126        active_pane_item: Option<&dyn ItemHandle>,
127        _window: &mut Window,
128        _: &mut Context<Self>,
129    ) -> ToolbarItemLocation {
130        if let Some(pane_item) = active_pane_item.as_ref() {
131            if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
132                self.editor = Some(Box::new(editor.downgrade()));
133                ToolbarItemLocation::PrimaryRight
134            } else if let Some(editor) = pane_item.downcast::<BufferDiagnosticsEditor>() {
135                self.editor = Some(Box::new(editor.downgrade()));
136                ToolbarItemLocation::PrimaryRight
137            } else {
138                ToolbarItemLocation::Hidden
139            }
140        } else {
141            ToolbarItemLocation::Hidden
142        }
143    }
144}
145
146impl Default for ToolbarControls {
147    fn default() -> Self {
148        Self::new()
149    }
150}
151
152impl ToolbarControls {
153    pub fn new() -> Self {
154        ToolbarControls { editor: None }
155    }
156
157    fn editor(&self) -> Option<&dyn DiagnosticsToolbarEditor> {
158        self.editor.as_deref()
159    }
160}