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