1use gpui::{AnyElement, Empty, Entity, FocusHandle, Focusable, ListState, Subscription, list};
2use project::debugger::session::{Session, SessionEvent};
3use ui::prelude::*;
4use util::maybe;
5
6pub(crate) struct LoadedSourceList {
7 list: ListState,
8 invalidate: bool,
9 focus_handle: FocusHandle,
10 _subscription: Subscription,
11 session: Entity<Session>,
12}
13
14impl LoadedSourceList {
15 pub fn new(session: Entity<Session>, cx: &mut Context<Self>) -> Self {
16 let focus_handle = cx.focus_handle();
17 let list = ListState::new(0, gpui::ListAlignment::Top, px(1000.));
18
19 let _subscription = cx.subscribe(&session, |this, _, event, cx| match event {
20 SessionEvent::Stopped(_)
21 | SessionEvent::HistoricSnapshotSelected
22 | SessionEvent::LoadedSources => {
23 this.invalidate = true;
24 cx.notify();
25 }
26 _ => {}
27 });
28
29 Self {
30 list,
31 session,
32 focus_handle,
33 _subscription,
34 invalidate: true,
35 }
36 }
37
38 fn render_entry(&mut self, ix: usize, cx: &mut Context<Self>) -> AnyElement {
39 let Some(source) = maybe!({
40 self.session
41 .update(cx, |state, cx| state.loaded_sources(cx).get(ix).cloned())
42 }) else {
43 return Empty.into_any();
44 };
45
46 v_flex()
47 .rounded_md()
48 .w_full()
49 .group("")
50 .p_1()
51 .hover(|s| s.bg(cx.theme().colors().element_hover))
52 .child(
53 h_flex()
54 .gap_0p5()
55 .text_ui_sm(cx)
56 .when_some(source.name.clone(), |this, name| this.child(name)),
57 )
58 .child(
59 h_flex()
60 .text_ui_xs(cx)
61 .text_color(cx.theme().colors().text_muted)
62 .when_some(source.path, |this, path| this.child(path)),
63 )
64 .into_any()
65 }
66}
67
68impl Focusable for LoadedSourceList {
69 fn focus_handle(&self, _: &gpui::App) -> gpui::FocusHandle {
70 self.focus_handle.clone()
71 }
72}
73
74impl Render for LoadedSourceList {
75 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
76 if self.invalidate {
77 let len = self
78 .session
79 .update(cx, |session, cx| session.loaded_sources(cx).len());
80 self.list.reset(len);
81 self.invalidate = false;
82 cx.notify();
83 }
84
85 div()
86 .track_focus(&self.focus_handle)
87 .size_full()
88 .p_1()
89 .child(
90 list(
91 self.list.clone(),
92 cx.processor(|this, ix, _window, cx| this.render_entry(ix, cx)),
93 )
94 .size_full(),
95 )
96 }
97}