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                .handle(self.session_picker_menu_handle.clone()),
137            )
138        } else {
139            None
140        }
141    }
142
143    pub(crate) fn render_thread_dropdown(
144        &self,
145        running_state: &Entity<RunningState>,
146        threads: Vec<(dap::Thread, ThreadStatus)>,
147        window: &mut Window,
148        cx: &mut Context<Self>,
149    ) -> Option<DropdownMenu> {
150        let running_state = running_state.clone();
151        let running_state_read = running_state.read(cx);
152        let thread_id = running_state_read.thread_id();
153        let session = running_state_read.session();
154        let session_id = session.read(cx).session_id();
155        let session_terminated = session.read(cx).is_terminated();
156        let selected_thread_name = threads
157            .iter()
158            .find(|(thread, _)| thread_id.map(|id| id.0) == Some(thread.id))
159            .map(|(thread, _)| {
160                thread
161                    .name
162                    .is_empty()
163                    .then(|| format!("Tid: {}", thread.id))
164                    .unwrap_or_else(|| thread.name.clone())
165            });
166
167        if let Some(selected_thread_name) = selected_thread_name {
168            let trigger = DebugPanel::dropdown_label(selected_thread_name).into_any_element();
169            Some(
170                DropdownMenu::new_with_element(
171                    ("thread-list", session_id.0),
172                    trigger,
173                    ContextMenu::build(window, cx, move |mut this, _, _| {
174                        for (thread, _) in threads {
175                            let running_state = running_state.clone();
176                            let thread_id = thread.id;
177                            let entry_name = thread
178                                .name
179                                .is_empty()
180                                .then(|| format!("Tid: {}", thread.id))
181                                .unwrap_or_else(|| thread.name);
182
183                            this = this.entry(entry_name, None, move |window, cx| {
184                                running_state.update(cx, |running_state, cx| {
185                                    running_state.select_thread(ThreadId(thread_id), window, cx);
186                                });
187                            });
188                        }
189                        this
190                    }),
191                )
192                .disabled(session_terminated)
193                .style(DropdownStyle::Ghost)
194                .handle(self.thread_picker_menu_handle.clone()),
195            )
196        } else {
197            None
198        }
199    }
200}