dropdown_menus.rs
1use gpui::Entity;
2use project::debugger::session::{ThreadId, ThreadStatus};
3use ui::{ContextMenu, DropdownMenu, DropdownStyle, Indicator, prelude::*};
4
5use crate::{
6 debugger_panel::DebugPanel,
7 session::{DebugSession, running::RunningState},
8};
9
10impl DebugPanel {
11 fn dropdown_label(label: impl Into<SharedString>) -> Label {
12 Label::new(label).size(LabelSize::Small)
13 }
14
15 pub fn render_session_menu(
16 &mut self,
17 active_session: Option<Entity<DebugSession>>,
18 running_state: Option<Entity<RunningState>>,
19 window: &mut Window,
20 cx: &mut Context<Self>,
21 ) -> Option<impl IntoElement> {
22 if let Some(running_state) = running_state {
23 let sessions = self.sessions().clone();
24 let weak = cx.weak_entity();
25 let running_state = running_state.read(cx);
26 let label = if let Some(active_session) = active_session {
27 active_session.read(cx).session(cx).read(cx).label()
28 } else {
29 SharedString::new_static("Unknown Session")
30 };
31
32 let is_terminated = running_state.session().read(cx).is_terminated();
33 let session_state_indicator = {
34 if is_terminated {
35 Some(Indicator::dot().color(Color::Error))
36 } else {
37 match running_state.thread_status(cx).unwrap_or_default() {
38 project::debugger::session::ThreadStatus::Stopped => {
39 Some(Indicator::dot().color(Color::Conflict))
40 }
41 _ => Some(Indicator::dot().color(Color::Success)),
42 }
43 }
44 };
45
46 let trigger = h_flex()
47 .gap_2()
48 .when_some(session_state_indicator, |this, indicator| {
49 this.child(indicator)
50 })
51 .justify_between()
52 .child(
53 DebugPanel::dropdown_label(label)
54 .when(is_terminated, |this| this.strikethrough()),
55 )
56 .into_any_element();
57
58 Some(
59 DropdownMenu::new_with_element(
60 "debugger-session-list",
61 trigger,
62 ContextMenu::build(window, cx, move |mut this, _, cx| {
63 let context_menu = cx.weak_entity();
64 for session in sessions.into_iter() {
65 let weak_session = session.downgrade();
66 let weak_session_id = weak_session.entity_id();
67
68 this = this.custom_entry(
69 {
70 let weak = weak.clone();
71 let context_menu = context_menu.clone();
72 move |_, cx| {
73 weak_session
74 .read_with(cx, |session, cx| {
75 let context_menu = context_menu.clone();
76 let id: SharedString = format!(
77 "debug-session-{}",
78 session.session_id(cx).0
79 )
80 .into();
81 h_flex()
82 .w_full()
83 .group(id.clone())
84 .justify_between()
85 .child(session.label_element(cx))
86 .child(
87 IconButton::new(
88 "close-debug-session",
89 IconName::Close,
90 )
91 .visible_on_hover(id.clone())
92 .icon_size(IconSize::Small)
93 .on_click({
94 let weak = weak.clone();
95 move |_, window, cx| {
96 weak.update(cx, |panel, cx| {
97 panel.close_session(
98 weak_session_id,
99 window,
100 cx,
101 );
102 })
103 .ok();
104 context_menu
105 .update(cx, |this, cx| {
106 this.cancel(
107 &Default::default(),
108 window,
109 cx,
110 );
111 })
112 .ok();
113 }
114 }),
115 )
116 .into_any_element()
117 })
118 .unwrap_or_else(|_| div().into_any_element())
119 }
120 },
121 {
122 let weak = weak.clone();
123 move |window, cx| {
124 weak.update(cx, |panel, cx| {
125 panel.activate_session(session.clone(), window, cx);
126 })
127 .ok();
128 }
129 },
130 );
131 }
132 this
133 }),
134 )
135 .style(DropdownStyle::Ghost),
136 )
137 } else {
138 None
139 }
140 }
141
142 pub(crate) fn render_thread_dropdown(
143 &self,
144 running_state: &Entity<RunningState>,
145 threads: Vec<(dap::Thread, ThreadStatus)>,
146 window: &mut Window,
147 cx: &mut Context<Self>,
148 ) -> Option<DropdownMenu> {
149 let running_state = running_state.clone();
150 let running_state_read = running_state.read(cx);
151 let thread_id = running_state_read.thread_id();
152 let session = running_state_read.session();
153 let session_id = session.read(cx).session_id();
154 let session_terminated = session.read(cx).is_terminated();
155 let selected_thread_name = threads
156 .iter()
157 .find(|(thread, _)| thread_id.map(|id| id.0) == Some(thread.id))
158 .map(|(thread, _)| thread.name.clone());
159
160 if let Some(selected_thread_name) = selected_thread_name {
161 let trigger = DebugPanel::dropdown_label(selected_thread_name).into_any_element();
162 Some(
163 DropdownMenu::new_with_element(
164 ("thread-list", session_id.0),
165 trigger,
166 ContextMenu::build_eager(window, cx, move |mut this, _, _| {
167 for (thread, _) in threads {
168 let running_state = running_state.clone();
169 let thread_id = thread.id;
170 this = this.entry(thread.name, None, move |window, cx| {
171 running_state.update(cx, |running_state, cx| {
172 running_state.select_thread(ThreadId(thread_id), window, cx);
173 });
174 });
175 }
176 this
177 }),
178 )
179 .disabled(session_terminated)
180 .style(DropdownStyle::Ghost),
181 )
182 } else {
183 None
184 }
185 }
186}