debugger: Tidy up dropdown menus (#30679)

Nate Butler created

Before
![CleanShot 2025-05-14 at 13 22
44@2x](https://github.com/user-attachments/assets/c6c06c5c-571d-4913-a691-161f44bba27c)

After
![CleanShot 2025-05-14 at 13 22
17@2x](https://github.com/user-attachments/assets/0a25a053-81a3-4b96-8963-4b770b1e5b45)

Release Notes:

- N/A

Change summary

crates/debugger_ui/src/debugger_panel.rs  | 190 ++++++++----------------
crates/debugger_ui/src/debugger_ui.rs     |   1 
crates/debugger_ui/src/dropdown_menus.rs  | 186 ++++++++++++++++++++++++
crates/debugger_ui/src/session.rs         |  12 
crates/debugger_ui/src/session/running.rs |  56 ++-----
crates/ui/src/components/dropdown_menu.rs |  53 ++++++
6 files changed, 329 insertions(+), 169 deletions(-)

Detailed changes

crates/debugger_ui/src/debugger_panel.rs 🔗

@@ -1,5 +1,6 @@
 use crate::persistence::DebuggerPaneItem;
 use crate::session::DebugSession;
+use crate::session::running::RunningState;
 use crate::{
     ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
     FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, Pause, Restart,
@@ -30,7 +31,7 @@ use settings::Settings;
 use std::any::TypeId;
 use std::sync::Arc;
 use task::{DebugScenario, TaskContext};
-use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
+use ui::{ContextMenu, Divider, Tooltip, prelude::*};
 use workspace::SplitDirection;
 use workspace::{
     Pane, Workspace,
@@ -87,7 +88,20 @@ impl DebugPanel {
         })
     }
 
-    fn filter_action_types(&self, cx: &mut App) {
+    pub(crate) fn sessions(&self) -> Vec<Entity<DebugSession>> {
+        self.sessions.clone()
+    }
+
+    pub fn active_session(&self) -> Option<Entity<DebugSession>> {
+        self.active_session.clone()
+    }
+
+    pub(crate) fn running_state(&self, cx: &mut App) -> Option<Entity<RunningState>> {
+        self.active_session()
+            .map(|session| session.read(cx).running_state().clone())
+    }
+
+    pub(crate) fn filter_action_types(&self, cx: &mut App) {
         let (has_active_session, supports_restart, support_step_back, status) = self
             .active_session()
             .map(|item| {
@@ -273,7 +287,7 @@ impl DebugPanel {
         .detach_and_log_err(cx);
     }
 
-    async fn register_session(
+    pub(crate) async fn register_session(
         this: WeakEntity<Self>,
         session: Entity<Session>,
         cx: &mut AsyncWindowContext,
@@ -342,7 +356,7 @@ impl DebugPanel {
         Ok(debug_session)
     }
 
-    fn handle_restart_request(
+    pub(crate) fn handle_restart_request(
         &mut self,
         mut curr_session: Entity<Session>,
         window: &mut Window,
@@ -416,11 +430,12 @@ impl DebugPanel {
         .detach_and_log_err(cx);
     }
 
-    pub fn active_session(&self) -> Option<Entity<DebugSession>> {
-        self.active_session.clone()
-    }
-
-    fn close_session(&mut self, entity_id: EntityId, window: &mut Window, cx: &mut Context<Self>) {
+    pub(crate) fn close_session(
+        &mut self,
+        entity_id: EntityId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         let Some(session) = self
             .sessions
             .iter()
@@ -474,93 +489,8 @@ impl DebugPanel {
         })
         .detach();
     }
-    fn sessions_drop_down_menu(
-        &self,
-        active_session: &Entity<DebugSession>,
-        window: &mut Window,
-        cx: &mut Context<Self>,
-    ) -> DropdownMenu {
-        let sessions = self.sessions.clone();
-        let weak = cx.weak_entity();
-        let label = active_session.read(cx).label_element(cx);
-
-        DropdownMenu::new_with_element(
-            "debugger-session-list",
-            label,
-            ContextMenu::build(window, cx, move |mut this, _, cx| {
-                let context_menu = cx.weak_entity();
-                for session in sessions.into_iter() {
-                    let weak_session = session.downgrade();
-                    let weak_session_id = weak_session.entity_id();
-
-                    this = this.custom_entry(
-                        {
-                            let weak = weak.clone();
-                            let context_menu = context_menu.clone();
-                            move |_, cx| {
-                                weak_session
-                                    .read_with(cx, |session, cx| {
-                                        let context_menu = context_menu.clone();
-                                        let id: SharedString =
-                                            format!("debug-session-{}", session.session_id(cx).0)
-                                                .into();
-                                        h_flex()
-                                            .w_full()
-                                            .group(id.clone())
-                                            .justify_between()
-                                            .child(session.label_element(cx))
-                                            .child(
-                                                IconButton::new(
-                                                    "close-debug-session",
-                                                    IconName::Close,
-                                                )
-                                                .visible_on_hover(id.clone())
-                                                .icon_size(IconSize::Small)
-                                                .on_click({
-                                                    let weak = weak.clone();
-                                                    move |_, window, cx| {
-                                                        weak.update(cx, |panel, cx| {
-                                                            panel.close_session(
-                                                                weak_session_id,
-                                                                window,
-                                                                cx,
-                                                            );
-                                                        })
-                                                        .ok();
-                                                        context_menu
-                                                            .update(cx, |this, cx| {
-                                                                this.cancel(
-                                                                    &Default::default(),
-                                                                    window,
-                                                                    cx,
-                                                                );
-                                                            })
-                                                            .ok();
-                                                    }
-                                                }),
-                                            )
-                                            .into_any_element()
-                                    })
-                                    .unwrap_or_else(|_| div().into_any_element())
-                            }
-                        },
-                        {
-                            let weak = weak.clone();
-                            move |window, cx| {
-                                weak.update(cx, |panel, cx| {
-                                    panel.activate_session(session.clone(), window, cx);
-                                })
-                                .ok();
-                            }
-                        },
-                    );
-                }
-                this
-            }),
-        )
-    }
 
-    fn deploy_context_menu(
+    pub(crate) fn deploy_context_menu(
         &mut self,
         position: Point<Pixels>,
         window: &mut Window,
@@ -611,7 +541,11 @@ impl DebugPanel {
         }
     }
 
-    fn top_controls_strip(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Div> {
+    pub(crate) fn top_controls_strip(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<Div> {
         let active_session = self.active_session.clone();
         let focus_handle = self.focus_handle.clone();
         let is_side = self.position(window, cx).axis() == gpui::Axis::Horizontal;
@@ -651,12 +585,12 @@ impl DebugPanel {
                                 active_session
                                     .as_ref()
                                     .map(|session| session.read(cx).running_state()),
-                                |this, running_session| {
+                                |this, running_state| {
                                     let thread_status =
-                                        running_session.read(cx).thread_status(cx).unwrap_or(
+                                        running_state.read(cx).thread_status(cx).unwrap_or(
                                             project::debugger::session::ThreadStatus::Exited,
                                         );
-                                    let capabilities = running_session.read(cx).capabilities(cx);
+                                    let capabilities = running_state.read(cx).capabilities(cx);
                                     this.map(|this| {
                                         if thread_status == ThreadStatus::Running {
                                             this.child(
@@ -667,7 +601,7 @@ impl DebugPanel {
                                                 .icon_size(IconSize::XSmall)
                                                 .shape(ui::IconButtonShape::Square)
                                                 .on_click(window.listener_for(
-                                                    &running_session,
+                                                    &running_state,
                                                     |this, _, _window, cx| {
                                                         this.pause_thread(cx);
                                                     },
@@ -694,7 +628,7 @@ impl DebugPanel {
                                                 .icon_size(IconSize::XSmall)
                                                 .shape(ui::IconButtonShape::Square)
                                                 .on_click(window.listener_for(
-                                                    &running_session,
+                                                    &running_state,
                                                     |this, _, _window, cx| this.continue_thread(cx),
                                                 ))
                                                 .disabled(thread_status != ThreadStatus::Stopped)
@@ -718,7 +652,7 @@ impl DebugPanel {
                                             .icon_size(IconSize::XSmall)
                                             .shape(ui::IconButtonShape::Square)
                                             .on_click(window.listener_for(
-                                                &running_session,
+                                                &running_state,
                                                 |this, _, _window, cx| {
                                                     this.step_over(cx);
                                                 },
@@ -742,7 +676,7 @@ impl DebugPanel {
                                             .icon_size(IconSize::XSmall)
                                             .shape(ui::IconButtonShape::Square)
                                             .on_click(window.listener_for(
-                                                &running_session,
+                                                &running_state,
                                                 |this, _, _window, cx| {
                                                     this.step_out(cx);
                                                 },
@@ -769,7 +703,7 @@ impl DebugPanel {
                                         .icon_size(IconSize::XSmall)
                                         .shape(ui::IconButtonShape::Square)
                                         .on_click(window.listener_for(
-                                            &running_session,
+                                            &running_state,
                                             |this, _, _window, cx| {
                                                 this.step_in(cx);
                                             },
@@ -819,7 +753,7 @@ impl DebugPanel {
                                                 || thread_status == ThreadStatus::Ended,
                                         )
                                         .on_click(window.listener_for(
-                                            &running_session,
+                                            &running_state,
                                             |this, _, _window, cx| {
                                                 this.toggle_ignore_breakpoints(cx);
                                             },
@@ -842,7 +776,7 @@ impl DebugPanel {
                                         IconButton::new("debug-restart", IconName::DebugRestart)
                                             .icon_size(IconSize::XSmall)
                                             .on_click(window.listener_for(
-                                                &running_session,
+                                                &running_state,
                                                 |this, _, _window, cx| {
                                                     this.restart_session(cx);
                                                 },
@@ -864,7 +798,7 @@ impl DebugPanel {
                                         IconButton::new("debug-stop", IconName::Power)
                                             .icon_size(IconSize::XSmall)
                                             .on_click(window.listener_for(
-                                                &running_session,
+                                                &running_state,
                                                 |this, _, _window, cx| {
                                                     this.stop_thread(cx);
                                                 },
@@ -898,7 +832,7 @@ impl DebugPanel {
                                         IconButton::new("debug-disconnect", IconName::DebugDetach)
                                             .icon_size(IconSize::XSmall)
                                             .on_click(window.listener_for(
-                                                &running_session,
+                                                &running_state,
                                                 |this, _, _, cx| {
                                                     this.detach_client(cx);
                                                 },
@@ -932,30 +866,42 @@ impl DebugPanel {
                                     .as_ref()
                                     .map(|session| session.read(cx).running_state())
                                     .cloned(),
-                                |this, session| {
-                                    this.child(
-                                        session.update(cx, |this, cx| {
-                                            this.thread_dropdown(window, cx)
-                                        }),
-                                    )
+                                |this, running_state| {
+                                    this.children({
+                                        let running_state = running_state.clone();
+                                        let threads =
+                                            running_state.update(cx, |running_state, cx| {
+                                                let session = running_state.session();
+                                                session
+                                                    .update(cx, |session, cx| session.threads(cx))
+                                            });
+
+                                        self.render_thread_dropdown(
+                                            &running_state,
+                                            threads,
+                                            window,
+                                            cx,
+                                        )
+                                    })
                                     .when(!is_side, |this| this.gap_2().child(Divider::vertical()))
                                 },
                             ),
                         )
                         .child(
                             h_flex()
-                                .when_some(active_session.as_ref(), |this, session| {
-                                    let context_menu =
-                                        self.sessions_drop_down_menu(session, window, cx);
-                                    this.child(context_menu).gap_2().child(Divider::vertical())
-                                })
+                                .children(self.render_session_menu(
+                                    self.active_session(),
+                                    self.running_state(cx),
+                                    window,
+                                    cx,
+                                ))
                                 .when(!is_side, |this| this.child(new_session_button())),
                         ),
                 ),
         )
     }
 
-    fn activate_pane_in_direction(
+    pub(crate) fn activate_pane_in_direction(
         &mut self,
         direction: SplitDirection,
         window: &mut Window,
@@ -970,7 +916,7 @@ impl DebugPanel {
         }
     }
 
-    fn activate_item(
+    pub(crate) fn activate_item(
         &mut self,
         item: DebuggerPaneItem,
         window: &mut Window,
@@ -985,7 +931,7 @@ impl DebugPanel {
         }
     }
 
-    fn activate_session(
+    pub(crate) fn activate_session(
         &mut self,
         session_item: Entity<DebugSession>,
         window: &mut Window,

crates/debugger_ui/src/debugger_ui.rs 🔗

@@ -13,6 +13,7 @@ use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
 
 pub mod attach_modal;
 pub mod debugger_panel;
+mod dropdown_menus;
 mod new_session_modal;
 mod persistence;
 pub(crate) mod session;

crates/debugger_ui/src/dropdown_menus.rs 🔗

@@ -0,0 +1,186 @@
+use gpui::Entity;
+use project::debugger::session::{ThreadId, ThreadStatus};
+use ui::{ContextMenu, DropdownMenu, DropdownStyle, Indicator, prelude::*};
+
+use crate::{
+    debugger_panel::DebugPanel,
+    session::{DebugSession, running::RunningState},
+};
+
+impl DebugPanel {
+    fn dropdown_label(label: impl Into<SharedString>) -> Label {
+        Label::new(label).size(LabelSize::Small)
+    }
+
+    pub fn render_session_menu(
+        &mut self,
+        active_session: Option<Entity<DebugSession>>,
+        running_state: Option<Entity<RunningState>>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<impl IntoElement> {
+        if let Some(running_state) = running_state {
+            let sessions = self.sessions().clone();
+            let weak = cx.weak_entity();
+            let running_state = running_state.read(cx);
+            let label = if let Some(active_session) = active_session {
+                active_session.read(cx).session(cx).read(cx).label()
+            } else {
+                SharedString::new_static("Unknown Session")
+            };
+
+            let is_terminated = running_state.session().read(cx).is_terminated();
+            let session_state_indicator = {
+                if is_terminated {
+                    Some(Indicator::dot().color(Color::Error))
+                } else {
+                    match running_state.thread_status(cx).unwrap_or_default() {
+                        project::debugger::session::ThreadStatus::Stopped => {
+                            Some(Indicator::dot().color(Color::Conflict))
+                        }
+                        _ => Some(Indicator::dot().color(Color::Success)),
+                    }
+                }
+            };
+
+            let trigger = h_flex()
+                .gap_2()
+                .when_some(session_state_indicator, |this, indicator| {
+                    this.child(indicator)
+                })
+                .justify_between()
+                .child(
+                    DebugPanel::dropdown_label(label)
+                        .when(is_terminated, |this| this.strikethrough()),
+                )
+                .into_any_element();
+
+            Some(
+                DropdownMenu::new_with_element(
+                    "debugger-session-list",
+                    trigger,
+                    ContextMenu::build(window, cx, move |mut this, _, cx| {
+                        let context_menu = cx.weak_entity();
+                        for session in sessions.into_iter() {
+                            let weak_session = session.downgrade();
+                            let weak_session_id = weak_session.entity_id();
+
+                            this = this.custom_entry(
+                                {
+                                    let weak = weak.clone();
+                                    let context_menu = context_menu.clone();
+                                    move |_, cx| {
+                                        weak_session
+                                            .read_with(cx, |session, cx| {
+                                                let context_menu = context_menu.clone();
+                                                let id: SharedString = format!(
+                                                    "debug-session-{}",
+                                                    session.session_id(cx).0
+                                                )
+                                                .into();
+                                                h_flex()
+                                                    .w_full()
+                                                    .group(id.clone())
+                                                    .justify_between()
+                                                    .child(session.label_element(cx))
+                                                    .child(
+                                                        IconButton::new(
+                                                            "close-debug-session",
+                                                            IconName::Close,
+                                                        )
+                                                        .visible_on_hover(id.clone())
+                                                        .icon_size(IconSize::Small)
+                                                        .on_click({
+                                                            let weak = weak.clone();
+                                                            move |_, window, cx| {
+                                                                weak.update(cx, |panel, cx| {
+                                                                    panel.close_session(
+                                                                        weak_session_id,
+                                                                        window,
+                                                                        cx,
+                                                                    );
+                                                                })
+                                                                .ok();
+                                                                context_menu
+                                                                    .update(cx, |this, cx| {
+                                                                        this.cancel(
+                                                                            &Default::default(),
+                                                                            window,
+                                                                            cx,
+                                                                        );
+                                                                    })
+                                                                    .ok();
+                                                            }
+                                                        }),
+                                                    )
+                                                    .into_any_element()
+                                            })
+                                            .unwrap_or_else(|_| div().into_any_element())
+                                    }
+                                },
+                                {
+                                    let weak = weak.clone();
+                                    move |window, cx| {
+                                        weak.update(cx, |panel, cx| {
+                                            panel.activate_session(session.clone(), window, cx);
+                                        })
+                                        .ok();
+                                    }
+                                },
+                            );
+                        }
+                        this
+                    }),
+                )
+                .style(DropdownStyle::Ghost),
+            )
+        } else {
+            None
+        }
+    }
+
+    pub(crate) fn render_thread_dropdown(
+        &self,
+        running_state: &Entity<RunningState>,
+        threads: Vec<(dap::Thread, ThreadStatus)>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<DropdownMenu> {
+        let running_state = running_state.clone();
+        let running_state_read = running_state.read(cx);
+        let thread_id = running_state_read.thread_id();
+        let session = running_state_read.session();
+        let session_id = session.read(cx).session_id();
+        let session_terminated = session.read(cx).is_terminated();
+        let selected_thread_name = threads
+            .iter()
+            .find(|(thread, _)| thread_id.map(|id| id.0) == Some(thread.id))
+            .map(|(thread, _)| thread.name.clone());
+
+        if let Some(selected_thread_name) = selected_thread_name {
+            let trigger = DebugPanel::dropdown_label(selected_thread_name).into_any_element();
+            Some(
+                DropdownMenu::new_with_element(
+                    ("thread-list", session_id.0),
+                    trigger,
+                    ContextMenu::build_eager(window, cx, move |mut this, _, _| {
+                        for (thread, _) in threads {
+                            let running_state = running_state.clone();
+                            let thread_id = thread.id;
+                            this = this.entry(thread.name, None, move |window, cx| {
+                                running_state.update(cx, |running_state, cx| {
+                                    running_state.select_thread(ThreadId(thread_id), window, cx);
+                                });
+                            });
+                        }
+                        this
+                    }),
+                )
+                .disabled(session_terminated)
+                .style(DropdownStyle::Ghost),
+            )
+        } else {
+            None
+        }
+    }
+}

crates/debugger_ui/src/session.rs 🔗

@@ -1,7 +1,6 @@
 pub mod running;
 
-use std::{cell::OnceCell, sync::OnceLock};
-
+use crate::{StackTraceView, debugger_panel::DebugPanel, persistence::SerializedLayout};
 use dap::client::SessionId;
 use gpui::{
     App, Axis, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity,
@@ -11,14 +10,13 @@ use project::debugger::session::Session;
 use project::worktree_store::WorktreeStore;
 use rpc::proto;
 use running::RunningState;
+use std::{cell::OnceCell, sync::OnceLock};
 use ui::{Indicator, prelude::*};
 use workspace::{
     CollaboratorId, FollowableItem, ViewId, Workspace,
     item::{self, Item},
 };
 
-use crate::{StackTraceView, debugger_panel::DebugPanel, persistence::SerializedLayout};
-
 pub struct DebugSession {
     remote_id: Option<workspace::ViewId>,
     running_state: Entity<RunningState>,
@@ -159,7 +157,11 @@ impl DebugSession {
             .gap_2()
             .when_some(icon, |this, indicator| this.child(indicator))
             .justify_between()
-            .child(Label::new(label).when(is_terminated, |this| this.strikethrough()))
+            .child(
+                Label::new(label)
+                    .size(LabelSize::Small)
+                    .when(is_terminated, |this| this.strikethrough()),
+            )
             .into_any_element()
     }
 }

crates/debugger_ui/src/session/running.rs 🔗

@@ -43,11 +43,10 @@ use task::{
 };
 use terminal_view::TerminalView;
 use ui::{
-    ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, ContextMenu,
-    Disableable, DropdownMenu, FluentBuilder, IconButton, IconName, IconSize, InteractiveElement,
-    IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,
-    StatefulInteractiveElement, Styled, Tab, Tooltip, VisibleOnHover, VisualContext, Window, div,
-    h_flex, v_flex,
+    ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, FluentBuilder,
+    IconButton, IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon as _,
+    ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, Tab, Tooltip,
+    VisibleOnHover, VisualContext, Window, div, h_flex, v_flex,
 };
 use util::ResultExt;
 use variable_list::VariableList;
@@ -78,6 +77,12 @@ pub struct RunningState {
     _schedule_serialize: Option<Task<()>>,
 }
 
+impl RunningState {
+    pub(crate) fn thread_id(&self) -> Option<ThreadId> {
+        self.thread_id
+    }
+}
+
 impl Render for RunningState {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let zoomed_pane = self
@@ -515,7 +520,7 @@ impl Focusable for DebugTerminal {
 }
 
 impl RunningState {
-    pub fn new(
+    pub(crate) fn new(
         session: Entity<Session>,
         project: Entity<Project>,
         workspace: WeakEntity<Workspace>,
@@ -1311,7 +1316,12 @@ impl RunningState {
             .map(|id| self.session().read(cx).thread_status(id))
     }
 
-    fn select_thread(&mut self, thread_id: ThreadId, window: &mut Window, cx: &mut Context<Self>) {
+    pub(crate) fn select_thread(
+        &mut self,
+        thread_id: ThreadId,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if self.thread_id.is_some_and(|id| id == thread_id) {
             return;
         }
@@ -1448,38 +1458,6 @@ impl RunningState {
         });
     }
 
-    pub(crate) fn thread_dropdown(
-        &self,
-        window: &mut Window,
-        cx: &mut Context<'_, RunningState>,
-    ) -> DropdownMenu {
-        let state = cx.entity();
-        let session_terminated = self.session.read(cx).is_terminated();
-        let threads = self.session.update(cx, |this, cx| this.threads(cx));
-        let selected_thread_name = threads
-            .iter()
-            .find(|(thread, _)| self.thread_id.map(|id| id.0) == Some(thread.id))
-            .map(|(thread, _)| thread.name.clone())
-            .unwrap_or("Threads".to_owned());
-        DropdownMenu::new(
-            ("thread-list", self.session_id.0),
-            selected_thread_name,
-            ContextMenu::build_eager(window, cx, move |mut this, _, _| {
-                for (thread, _) in threads {
-                    let state = state.clone();
-                    let thread_id = thread.id;
-                    this = this.entry(thread.name, None, move |window, cx| {
-                        state.update(cx, |state, cx| {
-                            state.select_thread(ThreadId(thread_id), window, cx);
-                        });
-                    });
-                }
-                this
-            }),
-        )
-        .disabled(session_terminated)
-    }
-
     fn default_pane_layout(
         project: Entity<Project>,
         workspace: &WeakEntity<Workspace>,

crates/ui/src/components/dropdown_menu.rs 🔗

@@ -1,7 +1,14 @@
-use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton};
+use gpui::{ClickEvent, Corner, CursorStyle, Entity, Hsla, MouseButton};
 
 use crate::{ContextMenu, PopoverMenu, prelude::*};
 
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum DropdownStyle {
+    #[default]
+    Solid,
+    Ghost,
+}
+
 enum LabelKind {
     Text(SharedString),
     Element(AnyElement),
@@ -11,6 +18,7 @@ enum LabelKind {
 pub struct DropdownMenu {
     id: ElementId,
     label: LabelKind,
+    style: DropdownStyle,
     menu: Entity<ContextMenu>,
     full_width: bool,
     disabled: bool,
@@ -25,6 +33,7 @@ impl DropdownMenu {
         Self {
             id: id.into(),
             label: LabelKind::Text(label.into()),
+            style: DropdownStyle::default(),
             menu,
             full_width: false,
             disabled: false,
@@ -39,12 +48,18 @@ impl DropdownMenu {
         Self {
             id: id.into(),
             label: LabelKind::Element(label),
+            style: DropdownStyle::default(),
             menu,
             full_width: false,
             disabled: false,
         }
     }
 
+    pub fn style(mut self, style: DropdownStyle) -> Self {
+        self.style = style;
+        self
+    }
+
     pub fn full_width(mut self, full_width: bool) -> Self {
         self.full_width = full_width;
         self
@@ -66,7 +81,8 @@ impl RenderOnce for DropdownMenu {
             .trigger(
                 DropdownMenuTrigger::new(self.label)
                     .full_width(self.full_width)
-                    .disabled(self.disabled),
+                    .disabled(self.disabled)
+                    .style(self.style),
             )
             .attach(Corner::BottomLeft)
     }
@@ -135,12 +151,35 @@ impl Component for DropdownMenu {
     }
 }
 
+#[derive(Debug, Clone, Copy)]
+pub struct DropdownTriggerStyle {
+    pub bg: Hsla,
+}
+
+impl DropdownTriggerStyle {
+    pub fn for_style(style: DropdownStyle, cx: &App) -> Self {
+        let colors = cx.theme().colors();
+
+        if style == DropdownStyle::Solid {
+            Self {
+                // why is this editor_background?
+                bg: colors.editor_background,
+            }
+        } else {
+            Self {
+                bg: colors.ghost_element_background,
+            }
+        }
+    }
+}
+
 #[derive(IntoElement)]
 struct DropdownMenuTrigger {
     label: LabelKind,
     full_width: bool,
     selected: bool,
     disabled: bool,
+    style: DropdownStyle,
     cursor_style: CursorStyle,
     on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
 }
@@ -152,6 +191,7 @@ impl DropdownMenuTrigger {
             full_width: false,
             selected: false,
             disabled: false,
+            style: DropdownStyle::default(),
             cursor_style: CursorStyle::default(),
             on_click: None,
         }
@@ -161,6 +201,11 @@ impl DropdownMenuTrigger {
         self.full_width = full_width;
         self
     }
+
+    pub fn style(mut self, style: DropdownStyle) -> Self {
+        self.style = style;
+        self
+    }
 }
 
 impl Disableable for DropdownMenuTrigger {
@@ -193,11 +238,13 @@ impl RenderOnce for DropdownMenuTrigger {
     fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
         let disabled = self.disabled;
 
+        let style = DropdownTriggerStyle::for_style(self.style, cx);
+
         h_flex()
             .id("dropdown-menu-trigger")
             .justify_between()
             .rounded_sm()
-            .bg(cx.theme().colors().editor_background)
+            .bg(style.bg)
             .pl_2()
             .pr_1p5()
             .py_0p5()