pub(crate) mod breakpoint_list;pub(crate) mod console;pub(crate) mod loaded_source_list;pub(crate) mod memory_view;pub(crate) mod module_list;pub mod stack_frame_list;pub mod variable_list;use std::{ any::Any, path::PathBuf, sync::{Arc, LazyLock}, time::Duration,}; use crate::{ ToggleExpandItem, attach_modal::{AttachModal, ModalIntent}, new_process_modal::resolve_path, persistence::{self, DebuggerPaneItem, SerializedLayout}, session::running::memory_view::MemoryView,}; use anyhow::{Context as _, Result, anyhow, bail};use breakpoint_list::BreakpointList;use collections::{HashMap, IndexMap};use console::Console;use dap::{ Capabilities, DapRegistry, RunInTerminalRequestArguments, Thread, adapters::{DebugAdapterName, DebugTaskDefinition}, client::SessionId, debugger_settings::DebuggerSettings,};use futures::{SinkExt, channel::mpsc};use gpui::{ Action as _, AnyView, AppContext, Axis, Entity, EntityId, EventEmitter, FocusHandle, Focusable, NoAction, Pixels, Point, Subscription, Task, TaskExt, WeakEntity,};use language::Buffer;use loaded_source_list::LoadedSourceList;use module_list::ModuleList;use project::{ DebugScenarioContext, Project, WorktreeId, debugger::session::{self, Session, SessionEvent, SessionStateEvent, ThreadId, ThreadStatus},};use rpc::proto::ViewId;use serde_json::Value;use settings::Settings;use stack_frame_list::StackFrameList;use task::{ BuildTaskDefinition, DebugScenario, SharedTaskContext, Shell, ShellBuilder, SpawnInTerminal, TaskContext, ZedDebugConfig, substitute_variables_in_str,};use terminal_view::TerminalView;use ui::{ FluentBuilder, IntoElement, Render, StatefulInteractiveElement, Tab, Tooltip, VisibleOnHover, VisualContext, prelude::*,};use util::ResultExt;use variable_list::VariableList;use workspace::{ ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, SplitDirection, Workspace, item::TabContentParams, move_item, pane::Event,}; static PROCESS_ID_PLACEHOLDER: LazyLock<String> = LazyLock::new(|| task::VariableName::PickProcessId.template_value()); pub struct RunningState { session: Entity<Session>, thread_id: Option<ThreadId>, focus_handle: FocusHandle, _remote_id: Option<ViewId>, workspace: WeakEntity<Workspace>, project: WeakEntity<Project>, session_id: SessionId, variable_list: Entity<variable_list::VariableList>, _subscriptions: Vec<Subscription>, stack_frame_list: Entity<stack_frame_list::StackFrameList>, loaded_sources_list: Entity<LoadedSourceList>, pub debug_terminal: Entity<DebugTerminal>, module_list: Entity<module_list::ModuleList>, console: Entity<Console>, breakpoint_list: Entity<BreakpointList>, panes: PaneGroup, active_pane: Entity<Pane>, pane_close_subscriptions: HashMap<EntityId, Subscription>, dock_axis: Axis, _schedule_serialize: Option<Task<()>>, pub(crate) scenario: Option<DebugScenario>, pub(crate) scenario_context: Option<DebugScenarioContext>, memory_view: Entity<MemoryView>,} impl RunningState { pub(crate) fn thread_id(&self) -> Option<ThreadId> { self.thread_id } pub(crate) fn active_pane(&self) -> &Entity<Pane> { &self.active_pane }} impl Render for RunningState { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let zoomed_pane = self .panes .panes() .into_iter() .find(|pane| pane.read(cx).is_zoomed()); let active = self.panes.panes().into_iter().next(); let pane = if let Some(zoomed_pane) = zoomed_pane { zoomed_pane.update(cx, |pane, cx| pane.render(window, cx).into_any_element()) } else if let Some(active) = active { self.panes .render( None, None, &ActivePaneDecorator::new(active, &self.workspace), window, cx, ) .into_any_element() } else { div().into_any_element() }; let thread_status = self .thread_id .map(|thread_id| self.session.read(cx).thread_status(thread_id)) .unwrap_or(ThreadStatus::Exited); self.variable_list.update(cx, |this, cx| { this.disabled(thread_status != ThreadStatus::Stopped, cx); }); v_flex() .size_full() .key_context("DebugSessionItem") .track_focus(&self.focus_handle(cx)) .child(h_flex().flex_1().child(pane)) }} pub(crate) struct SubView { inner: AnyView, item_focus_handle: FocusHandle, kind: DebuggerPaneItem, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, show_indicator: Box<dyn Fn(&App) -> bool>, actions: Option<Box<dyn FnMut(&mut Window, &mut App) -> AnyElement>>, hovered: bool,} impl SubView { pub(crate) fn new( item_focus_handle: FocusHandle, view: AnyView, kind: DebuggerPaneItem, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { cx.new(|_| Self { kind, inner: view, item_focus_handle, running_state, host_pane, show_indicator: Box::new(|_| false), actions: None, hovered: false, }) } pub(crate) fn stack_frame_list( stack_frame_list: Entity<StackFrameList>, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { let weak_list = stack_frame_list.downgrade(); let this = Self::new( stack_frame_list.focus_handle(cx), stack_frame_list.into(), DebuggerPaneItem::Frames, running_state, host_pane, cx, ); this.update(cx, |this, _| { this.with_actions(Box::new(move |_, cx| { weak_list .update(cx, |this, _| this.render_control_strip()) .unwrap_or_else(|_| div().into_any_element()) })); }); this } pub(crate) fn console( console: Entity<Console>, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { let weak_console = console.downgrade(); let this = Self::new( console.focus_handle(cx), console.into(), DebuggerPaneItem::Console, running_state, host_pane, cx, ); this.update(cx, |this, _| { this.with_indicator(Box::new(move |cx| { weak_console .read_with(cx, |console, cx| console.show_indicator(cx)) .unwrap_or_default() })) }); this } pub(crate) fn breakpoint_list( list: Entity<BreakpointList>, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { let weak_list = list.downgrade(); let focus_handle = list.focus_handle(cx); let this = Self::new( focus_handle, list.into(), DebuggerPaneItem::BreakpointList, running_state, host_pane, cx, ); this.update(cx, |this, _| { this.with_actions(Box::new(move |_, cx| { weak_list .update(cx, |this, _| this.render_control_strip()) .unwrap_or_else(|_| div().into_any_element()) })); }); this } pub(crate) fn view_kind(&self) -> DebuggerPaneItem { self.kind } pub(crate) fn with_indicator(&mut self, indicator: Box<dyn Fn(&App) -> bool>) { self.show_indicator = indicator; } pub(crate) fn with_actions( &mut self, actions: Box<dyn FnMut(&mut Window, &mut App) -> AnyElement>, ) { self.actions = Some(actions); } fn set_host_pane(&mut self, host_pane: WeakEntity<Pane>) { self.host_pane = host_pane; }}impl Focusable for SubView { fn focus_handle(&self, _: &App) -> FocusHandle { self.item_focus_handle.clone() }}impl EventEmitter<()> for SubView {}impl Item for SubView { type Event = (); /// This is used to serialize debugger pane layouts /// A SharedString gets converted to a enum and back during serialization/deserialization. fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString { self.kind.to_shared_string() } fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> { Some(self.kind.tab_tooltip()) } fn tab_content( &self, params: workspace::item::TabContentParams, _: &Window, cx: &App, ) -> AnyElement { let label = Label::new(self.kind.to_shared_string()) .size(ui::LabelSize::Small) .color(params.text_color()) .line_height_style(ui::LineHeightStyle::UiLabel); if !params.selected && self.show_indicator.as_ref()(cx) { return h_flex() .justify_between() .child(ui::Indicator::dot()) .gap_2() .child(label) .into_any_element(); } label.into_any_element() } fn handle_drop( &self, active_pane: &Pane, dropped: &dyn Any, window: &mut Window, cx: &mut App, ) -> bool { let Some(tab) = dropped.downcast_ref::<DraggedTab>() else { return true; }; let Some(this_pane) = self.host_pane.upgrade() else { return true; }; if tab.item.downcast::<SubView>().is_none() { return true; } let Some(split_direction) = active_pane.drag_split_direction() else { return false; }; let source = tab.pane.clone(); let item_id_to_move = tab.item.item_id(); let weak_running = self.running_state.clone(); // Source pane may be the one currently updated, so defer the move. window.defer(cx, move |window, cx| { let new_pane = weak_running.update(cx, |running, cx| { let Some(project) = running.project.upgrade() else { return Err(anyhow!("Debugger project has been dropped")); }; let new_pane = new_debugger_pane(running.workspace.clone(), project, window, cx); let _previous_subscription = running.pane_close_subscriptions.insert( new_pane.entity_id(), cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event), ); debug_assert!(_previous_subscription.is_none()); running .panes .split(&this_pane, &new_pane, split_direction, cx); anyhow::Ok(new_pane) }); match new_pane.and_then(|result| result) { Ok(new_pane) => { move_item( &source, &new_pane, item_id_to_move, new_pane.read(cx).active_item_index(), true, window, cx, ); } Err(err) => { log::error!("{err:?}"); } } }); true }} impl Render for SubView { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { v_flex() .id(format!( "subview-container-{}", self.kind.to_shared_string() )) .size_full() .border_1() .when(self.item_focus_handle.contains_focused(window, cx), |el| { el.border_color(cx.theme().colors().pane_focused_border) }) .child(self.inner.clone()) .on_hover(cx.listener(|this, hovered, _, cx| { this.hovered = *hovered; cx.notify(); })) }} struct DraggedTabPreview { label: SharedString,} impl Render for DraggedTabPreview { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let ui_font = theme_settings::ThemeSettings::get_global(cx) .ui_font .clone(); let colors = cx.theme().colors(); h_flex() .font(ui_font) .h_6() .px_1() .rounded_sm() .shadow_md() .border_1() .border_color(colors.border) .bg(colors.elevated_surface_background) .child(Label::new(self.label.clone()).size(LabelSize::Small)) }} fn render_debugger_tab( ix: usize, item: &dyn ItemHandle, selected: bool, deemphasized: bool, window: &mut Window, cx: &mut Context<Pane>,) -> impl IntoElement + use<> { let item_ = item.boxed_clone(); let colors = cx.theme().colors(); div() .border_l_2() .border_color(gpui::transparent_black()) .drag_over::<DraggedTab>(|wrapper, _, _, cx| wrapper.border_color(cx.theme().colors().text)) .child( div() .cursor_pointer() .id(format!("debugger_tab_{}", item.item_id().as_u64())) .p_1() .rounded_sm() .map(|s| { if selected { s.bg(colors.text_accent.opacity(0.08)) .hover(|s| s.bg(colors.text_accent.opacity(0.25))) .text_color(colors.text_accent) } else { s.hover(|s| s.bg(colors.element_hover)) } }) .when(deemphasized, |s| s.opacity(0.8)) .child(item.tab_content( TabContentParams { selected, deemphasized, ..Default::default() }, window, cx, )) .when_some(item.tab_tooltip_text(cx), |this, tooltip| { this.tooltip(Tooltip::text(tooltip)) }) .on_click(cx.listener(move |this, _, window, cx| { let index = this.index_for_item(&*item_); if let Some(index) = index { this.activate_item(index, true, true, window, cx); } })) .on_drop( cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| { if dragged_tab.item.downcast::<SubView>().is_none() { return; } this.drag_split_direction = None; this.handle_tab_drop(dragged_tab, ix, false, window, cx) }), ) .on_drag( DraggedTab { item: item.boxed_clone(), pane: cx.entity(), detail: 0, is_active: selected, ix, }, |tab, _, _, cx| { let label = tab.item.tab_content_text(0, cx); cx.new(|_| DraggedTabPreview { label }) }, ), )} fn render_debugger_tab_bar( pane: &mut Pane, focus_handle: &FocusHandle, window: &mut Window,Lines 1–500 of 2123. Load more
pub(crate) mod breakpoint_list; pub(crate) mod console; pub(crate) mod loaded_source_list; pub(crate) mod memory_view; pub(crate) mod module_list; pub mod stack_frame_list; pub mod variable_list; use std::{ any::Any, path::PathBuf, sync::{Arc, LazyLock}, time::Duration, }; use crate::{ ToggleExpandItem, attach_modal::{AttachModal, ModalIntent}, new_process_modal::resolve_path, persistence::{self, DebuggerPaneItem, SerializedLayout}, session::running::memory_view::MemoryView, }; use anyhow::{Context as _, Result, anyhow, bail}; use breakpoint_list::BreakpointList; use collections::{HashMap, IndexMap}; use console::Console; use dap::{ Capabilities, DapRegistry, RunInTerminalRequestArguments, Thread, adapters::{DebugAdapterName, DebugTaskDefinition}, client::SessionId, debugger_settings::DebuggerSettings, }; use futures::{SinkExt, channel::mpsc}; use gpui::{ Action as _, AnyView, AppContext, Axis, Entity, EntityId, EventEmitter, FocusHandle, Focusable, NoAction, Pixels, Point, Subscription, Task, TaskExt, WeakEntity, }; use language::Buffer; use loaded_source_list::LoadedSourceList; use module_list::ModuleList; use project::{ DebugScenarioContext, Project, WorktreeId, debugger::session::{self, Session, SessionEvent, SessionStateEvent, ThreadId, ThreadStatus}, }; use rpc::proto::ViewId; use serde_json::Value; use settings::Settings; use stack_frame_list::StackFrameList; use task::{ BuildTaskDefinition, DebugScenario, SharedTaskContext, Shell, ShellBuilder, SpawnInTerminal, TaskContext, ZedDebugConfig, substitute_variables_in_str, }; use terminal_view::TerminalView; use ui::{ FluentBuilder, IntoElement, Render, StatefulInteractiveElement, Tab, Tooltip, VisibleOnHover, VisualContext, prelude::*, }; use util::ResultExt; use variable_list::VariableList; use workspace::{ ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, SplitDirection, Workspace, item::TabContentParams, move_item, pane::Event, }; static PROCESS_ID_PLACEHOLDER: LazyLock<String> = LazyLock::new(|| task::VariableName::PickProcessId.template_value()); pub struct RunningState { session: Entity<Session>, thread_id: Option<ThreadId>, focus_handle: FocusHandle, _remote_id: Option<ViewId>, workspace: WeakEntity<Workspace>, project: WeakEntity<Project>, session_id: SessionId, variable_list: Entity<variable_list::VariableList>, _subscriptions: Vec<Subscription>, stack_frame_list: Entity<stack_frame_list::StackFrameList>, loaded_sources_list: Entity<LoadedSourceList>, pub debug_terminal: Entity<DebugTerminal>, module_list: Entity<module_list::ModuleList>, console: Entity<Console>, breakpoint_list: Entity<BreakpointList>, panes: PaneGroup, active_pane: Entity<Pane>, pane_close_subscriptions: HashMap<EntityId, Subscription>, dock_axis: Axis, _schedule_serialize: Option<Task<()>>, pub(crate) scenario: Option<DebugScenario>, pub(crate) scenario_context: Option<DebugScenarioContext>, memory_view: Entity<MemoryView>, } impl RunningState { pub(crate) fn thread_id(&self) -> Option<ThreadId> { self.thread_id } pub(crate) fn active_pane(&self) -> &Entity<Pane> { &self.active_pane } } impl Render for RunningState { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let zoomed_pane = self .panes .panes() .into_iter() .find(|pane| pane.read(cx).is_zoomed()); let active = self.panes.panes().into_iter().next(); let pane = if let Some(zoomed_pane) = zoomed_pane { zoomed_pane.update(cx, |pane, cx| pane.render(window, cx).into_any_element()) } else if let Some(active) = active { self.panes .render( None, None, &ActivePaneDecorator::new(active, &self.workspace), window, cx, ) .into_any_element() } else { div().into_any_element() }; let thread_status = self .thread_id .map(|thread_id| self.session.read(cx).thread_status(thread_id)) .unwrap_or(ThreadStatus::Exited); self.variable_list.update(cx, |this, cx| { this.disabled(thread_status != ThreadStatus::Stopped, cx); }); v_flex() .size_full() .key_context("DebugSessionItem") .track_focus(&self.focus_handle(cx)) .child(h_flex().flex_1().child(pane)) } } pub(crate) struct SubView { inner: AnyView, item_focus_handle: FocusHandle, kind: DebuggerPaneItem, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, show_indicator: Box<dyn Fn(&App) -> bool>, actions: Option<Box<dyn FnMut(&mut Window, &mut App) -> AnyElement>>, hovered: bool, } impl SubView { pub(crate) fn new( item_focus_handle: FocusHandle, view: AnyView, kind: DebuggerPaneItem, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { cx.new(|_| Self { kind, inner: view, item_focus_handle, running_state, host_pane, show_indicator: Box::new(|_| false), actions: None, hovered: false, }) } pub(crate) fn stack_frame_list( stack_frame_list: Entity<StackFrameList>, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { let weak_list = stack_frame_list.downgrade(); let this = Self::new( stack_frame_list.focus_handle(cx), stack_frame_list.into(), DebuggerPaneItem::Frames, running_state, host_pane, cx, ); this.update(cx, |this, _| { this.with_actions(Box::new(move |_, cx| { weak_list .update(cx, |this, _| this.render_control_strip()) .unwrap_or_else(|_| div().into_any_element()) })); }); this } pub(crate) fn console( console: Entity<Console>, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { let weak_console = console.downgrade(); let this = Self::new( console.focus_handle(cx), console.into(), DebuggerPaneItem::Console, running_state, host_pane, cx, ); this.update(cx, |this, _| { this.with_indicator(Box::new(move |cx| { weak_console .read_with(cx, |console, cx| console.show_indicator(cx)) .unwrap_or_default() })) }); this } pub(crate) fn breakpoint_list( list: Entity<BreakpointList>, running_state: WeakEntity<RunningState>, host_pane: WeakEntity<Pane>, cx: &mut App, ) -> Entity<Self> { let weak_list = list.downgrade(); let focus_handle = list.focus_handle(cx); let this = Self::new( focus_handle, list.into(), DebuggerPaneItem::BreakpointList, running_state, host_pane, cx, ); this.update(cx, |this, _| { this.with_actions(Box::new(move |_, cx| { weak_list .update(cx, |this, _| this.render_control_strip()) .unwrap_or_else(|_| div().into_any_element()) })); }); this } pub(crate) fn view_kind(&self) -> DebuggerPaneItem { self.kind } pub(crate) fn with_indicator(&mut self, indicator: Box<dyn Fn(&App) -> bool>) { self.show_indicator = indicator; } pub(crate) fn with_actions( &mut self, actions: Box<dyn FnMut(&mut Window, &mut App) -> AnyElement>, ) { self.actions = Some(actions); } fn set_host_pane(&mut self, host_pane: WeakEntity<Pane>) { self.host_pane = host_pane; } } impl Focusable for SubView { fn focus_handle(&self, _: &App) -> FocusHandle { self.item_focus_handle.clone() } } impl EventEmitter<()> for SubView {} impl Item for SubView { type Event = (); /// This is used to serialize debugger pane layouts /// A SharedString gets converted to a enum and back during serialization/deserialization. fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString { self.kind.to_shared_string() } fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> { Some(self.kind.tab_tooltip()) } fn tab_content( &self, params: workspace::item::TabContentParams, _: &Window, cx: &App, ) -> AnyElement { let label = Label::new(self.kind.to_shared_string()) .size(ui::LabelSize::Small) .color(params.text_color()) .line_height_style(ui::LineHeightStyle::UiLabel); if !params.selected && self.show_indicator.as_ref()(cx) { return h_flex() .justify_between() .child(ui::Indicator::dot()) .gap_2() .child(label) .into_any_element(); } label.into_any_element() } fn handle_drop( &self, active_pane: &Pane, dropped: &dyn Any, window: &mut Window, cx: &mut App, ) -> bool { let Some(tab) = dropped.downcast_ref::<DraggedTab>() else { return true; }; let Some(this_pane) = self.host_pane.upgrade() else { return true; }; if tab.item.downcast::<SubView>().is_none() { return true; } let Some(split_direction) = active_pane.drag_split_direction() else { return false; }; let source = tab.pane.clone(); let item_id_to_move = tab.item.item_id(); let weak_running = self.running_state.clone(); // Source pane may be the one currently updated, so defer the move. window.defer(cx, move |window, cx| { let new_pane = weak_running.update(cx, |running, cx| { let Some(project) = running.project.upgrade() else { return Err(anyhow!("Debugger project has been dropped")); }; let new_pane = new_debugger_pane(running.workspace.clone(), project, window, cx); let _previous_subscription = running.pane_close_subscriptions.insert( new_pane.entity_id(), cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event), ); debug_assert!(_previous_subscription.is_none()); running .panes .split(&this_pane, &new_pane, split_direction, cx); anyhow::Ok(new_pane) }); match new_pane.and_then(|result| result) { Ok(new_pane) => { move_item( &source, &new_pane, item_id_to_move, new_pane.read(cx).active_item_index(), true, window, cx, ); } Err(err) => { log::error!("{err:?}"); } } }); true } } impl Render for SubView { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { v_flex() .id(format!( "subview-container-{}", self.kind.to_shared_string() )) .size_full() .border_1() .when(self.item_focus_handle.contains_focused(window, cx), |el| { el.border_color(cx.theme().colors().pane_focused_border) }) .child(self.inner.clone()) .on_hover(cx.listener(|this, hovered, _, cx| { this.hovered = *hovered; cx.notify(); })) } } struct DraggedTabPreview { label: SharedString, } impl Render for DraggedTabPreview { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let ui_font = theme_settings::ThemeSettings::get_global(cx) .ui_font .clone(); let colors = cx.theme().colors(); h_flex() .font(ui_font) .h_6() .px_1() .rounded_sm() .shadow_md() .border_1() .border_color(colors.border) .bg(colors.elevated_surface_background) .child(Label::new(self.label.clone()).size(LabelSize::Small)) } } fn render_debugger_tab( ix: usize, item: &dyn ItemHandle, selected: bool, deemphasized: bool, window: &mut Window, cx: &mut Context<Pane>, ) -> impl IntoElement + use<> { let item_ = item.boxed_clone(); let colors = cx.theme().colors(); div() .border_l_2() .border_color(gpui::transparent_black()) .drag_over::<DraggedTab>(|wrapper, _, _, cx| wrapper.border_color(cx.theme().colors().text)) .child( div() .cursor_pointer() .id(format!("debugger_tab_{}", item.item_id().as_u64())) .p_1() .rounded_sm() .map(|s| { if selected { s.bg(colors.text_accent.opacity(0.08)) .hover(|s| s.bg(colors.text_accent.opacity(0.25))) .text_color(colors.text_accent) } else { s.hover(|s| s.bg(colors.element_hover)) } }) .when(deemphasized, |s| s.opacity(0.8)) .child(item.tab_content( TabContentParams { selected, deemphasized, ..Default::default() }, window, cx, )) .when_some(item.tab_tooltip_text(cx), |this, tooltip| { this.tooltip(Tooltip::text(tooltip)) }) .on_click(cx.listener(move |this, _, window, cx| { let index = this.index_for_item(&*item_); if let Some(index) = index { this.activate_item(index, true, true, window, cx); } })) .on_drop( cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| { if dragged_tab.item.downcast::<SubView>().is_none() { return; } this.drag_split_direction = None; this.handle_tab_drop(dragged_tab, ix, false, window, cx) }), ) .on_drag( DraggedTab { item: item.boxed_clone(), pane: cx.entity(), detail: 0, is_active: selected, ix, }, |tab, _, _, cx| { let label = tab.item.tab_content_text(0, cx); cx.new(|_| DraggedTabPreview { label }) }, ), ) } fn render_debugger_tab_bar( pane: &mut Pane, focus_handle: &FocusHandle, window: &mut Window,