1use gpui::{
2 elements::*, platform::CursorStyle, serde_json, Entity, ModelHandle, RenderContext, View,
3 ViewContext,
4};
5use project::Project;
6use settings::Settings;
7use workspace::StatusItemView;
8
9pub struct DiagnosticSummary {
10 summary: project::DiagnosticSummary,
11 in_progress: bool,
12}
13
14impl DiagnosticSummary {
15 pub fn new(project: &ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
16 cx.subscribe(project, |this, project, event, cx| match event {
17 project::Event::DiskBasedDiagnosticsUpdated => {
18 cx.notify();
19 }
20 project::Event::DiskBasedDiagnosticsStarted => {
21 this.in_progress = true;
22 cx.notify();
23 }
24 project::Event::DiskBasedDiagnosticsFinished => {
25 this.summary = project.read(cx).diagnostic_summary(cx);
26 this.in_progress = false;
27 cx.notify();
28 }
29 _ => {}
30 })
31 .detach();
32 Self {
33 summary: project.read(cx).diagnostic_summary(cx),
34 in_progress: project.read(cx).is_running_disk_based_diagnostics(),
35 }
36 }
37}
38
39impl Entity for DiagnosticSummary {
40 type Event = ();
41}
42
43impl View for DiagnosticSummary {
44 fn ui_name() -> &'static str {
45 "DiagnosticSummary"
46 }
47
48 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
49 enum Tag {}
50
51 let in_progress = self.in_progress;
52 MouseEventHandler::new::<Tag, _, _>(0, cx, |state, cx| {
53 let style = &cx
54 .global::<Settings>()
55 .theme
56 .workspace
57 .status_bar
58 .diagnostics;
59 let summary_style = if self.summary.error_count > 0 {
60 if state.hovered {
61 &style.summary_error_hover
62 } else {
63 &style.summary_error
64 }
65 } else if self.summary.warning_count > 0 {
66 if state.hovered {
67 &style.summary_warning_hover
68 } else {
69 &style.summary_warning
70 }
71 } else if state.hovered {
72 &style.summary_ok_hover
73 } else {
74 &style.summary_ok
75 };
76
77 let mut row = Flex::row();
78 if self.summary.error_count > 0 {
79 row.add_children([
80 Svg::new("icons/error-solid-14.svg")
81 .with_color(style.icon_color_error)
82 .constrained()
83 .with_width(style.icon_width)
84 .aligned()
85 .contained()
86 .with_margin_right(style.icon_spacing)
87 .named("error-icon"),
88 Label::new(
89 self.summary.error_count.to_string(),
90 summary_style.text.clone(),
91 )
92 .aligned()
93 .boxed(),
94 ]);
95 }
96
97 if self.summary.warning_count > 0 {
98 row.add_children([
99 Svg::new("icons/warning-solid-14.svg")
100 .with_color(style.icon_color_warning)
101 .constrained()
102 .with_width(style.icon_width)
103 .aligned()
104 .contained()
105 .with_margin_right(style.icon_spacing)
106 .with_margin_left(if self.summary.error_count > 0 {
107 style.summary_spacing
108 } else {
109 0.
110 })
111 .named("warning-icon"),
112 Label::new(
113 self.summary.warning_count.to_string(),
114 summary_style.text.clone(),
115 )
116 .aligned()
117 .boxed(),
118 ]);
119 }
120
121 if self.summary.error_count == 0 && self.summary.warning_count == 0 {
122 row.add_child(
123 Svg::new("icons/no-error-solid-14.svg")
124 .with_color(style.icon_color_ok)
125 .constrained()
126 .with_width(style.icon_width)
127 .aligned()
128 .named("ok-icon"),
129 );
130 }
131
132 row.constrained()
133 .with_height(style.height)
134 .contained()
135 .with_style(summary_style.container)
136 .boxed()
137 })
138 .with_cursor_style(CursorStyle::PointingHand)
139 .on_click(|cx| cx.dispatch_action(crate::Deploy))
140 .boxed()
141 }
142
143 fn debug_json(&self, _: &gpui::AppContext) -> serde_json::Value {
144 serde_json::json!({ "summary": self.summary })
145 }
146}
147
148impl StatusItemView for DiagnosticSummary {
149 fn set_active_pane_item(
150 &mut self,
151 _: Option<&dyn workspace::ItemHandle>,
152 _: &mut ViewContext<Self>,
153 ) {
154 }
155}