debugger: Style debugger tabs (#28572)
 Release Notes: - N/A
This commit is contained in:
parent
cdcad708f6
commit
e09eeb7446
4 changed files with 177 additions and 93 deletions
|
@ -26,13 +26,15 @@ use rpc::proto::ViewId;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use stack_frame_list::StackFrameList;
|
use stack_frame_list::StackFrameList;
|
||||||
use ui::{
|
use ui::{
|
||||||
AnyElement, App, Context, ContextMenu, DropdownMenu, InteractiveElement, IntoElement, Label,
|
ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder,
|
||||||
LabelCommon as _, ParentElement, Render, SharedString, Styled, Window, div, h_flex, v_flex,
|
InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,
|
||||||
|
StatefulInteractiveElement, Styled, Tab, Window, div, h_flex, v_flex,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use variable_list::VariableList;
|
use variable_list::VariableList;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
ActivePaneDecorator, DraggedTab, Item, Pane, PaneGroup, Workspace, move_item, pane::Event,
|
ActivePaneDecorator, DraggedTab, Item, Pane, PaneGroup, Workspace, item::TabContentParams,
|
||||||
|
move_item, pane::Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RunningState {
|
pub struct RunningState {
|
||||||
|
@ -121,8 +123,9 @@ impl Item for SubView {
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let label = Label::new(self.tab_name.clone())
|
let label = Label::new(self.tab_name.clone())
|
||||||
|
.size(ui::LabelSize::Small)
|
||||||
.color(params.text_color())
|
.color(params.text_color())
|
||||||
.into_any_element();
|
.line_height_style(ui::LineHeightStyle::UiLabel);
|
||||||
|
|
||||||
if !params.selected && self.show_indicator.as_ref()(cx) {
|
if !params.selected && self.show_indicator.as_ref()(cx) {
|
||||||
return h_flex()
|
return h_flex()
|
||||||
|
@ -133,7 +136,7 @@ impl Item for SubView {
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
}
|
}
|
||||||
|
|
||||||
label
|
label.into_any_element()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +269,79 @@ fn new_debugger_pane(
|
||||||
})));
|
})));
|
||||||
pane.display_nav_history_buttons(None);
|
pane.display_nav_history_buttons(None);
|
||||||
pane.set_custom_drop_handle(cx, custom_drop_handle);
|
pane.set_custom_drop_handle(cx, custom_drop_handle);
|
||||||
|
pane.set_should_display_tab_bar(|_, _| true);
|
||||||
pane.set_render_tab_bar_buttons(cx, |_, _, _| (None, None));
|
pane.set_render_tab_bar_buttons(cx, |_, _, _| (None, None));
|
||||||
|
pane.set_render_tab_bar(cx, |pane, window, cx| {
|
||||||
|
let active_pane_item = pane.active_item();
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.h(Tab::container_height(cx))
|
||||||
|
.drag_over::<DraggedTab>(|bar, _, _, cx| {
|
||||||
|
bar.bg(cx.theme().colors().drop_target_background)
|
||||||
|
})
|
||||||
|
.on_drop(
|
||||||
|
cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {
|
||||||
|
this.drag_split_direction = None;
|
||||||
|
this.handle_tab_drop(dragged_tab, this.items_len(), window, cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.bg(cx.theme().colors().tab_bar_background)
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.children(pane.items().enumerate().map(|(ix, item)| {
|
||||||
|
let selected = active_pane_item
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |active| active.item_id() == item.item_id());
|
||||||
|
let item_ = item.boxed_clone();
|
||||||
|
div()
|
||||||
|
.id(SharedString::from(format!(
|
||||||
|
"debugger_tab_{}",
|
||||||
|
item.item_id().as_u64()
|
||||||
|
)))
|
||||||
|
.p_1()
|
||||||
|
.rounded_md()
|
||||||
|
.cursor_pointer()
|
||||||
|
.map(|this| {
|
||||||
|
if selected {
|
||||||
|
this.bg(cx.theme().colors().tab_active_background)
|
||||||
|
} else {
|
||||||
|
let hover_color = cx.theme().colors().element_hover;
|
||||||
|
this.hover(|style| style.bg(hover_color))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.child(item.tab_content(
|
||||||
|
TabContentParams {
|
||||||
|
selected,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.on_drop(
|
||||||
|
cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {
|
||||||
|
this.drag_split_direction = None;
|
||||||
|
this.handle_tab_drop(dragged_tab, ix, window, cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_drag(
|
||||||
|
DraggedTab {
|
||||||
|
item: item.boxed_clone(),
|
||||||
|
pane: cx.entity().clone(),
|
||||||
|
detail: 0,
|
||||||
|
is_active: selected,
|
||||||
|
ix,
|
||||||
|
},
|
||||||
|
|tab, _, _, cx| cx.new(|_| tab.clone()),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
});
|
||||||
pane
|
pane
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -235,7 +235,6 @@ impl Render for Console {
|
||||||
.when(self.is_local(cx), |this| {
|
.when(self.is_local(cx), |this| {
|
||||||
this.child(Divider::horizontal())
|
this.child(Divider::horizontal())
|
||||||
.child(self.render_query_bar(cx))
|
.child(self.render_query_bar(cx))
|
||||||
.pt(DynamicSpacing::Base04.rems(cx))
|
|
||||||
})
|
})
|
||||||
.border_2()
|
.border_2()
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ pub struct BreadcrumbText {
|
||||||
pub font: Option<Font>,
|
pub font: Option<Font>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
pub struct TabContentParams {
|
pub struct TabContentParams {
|
||||||
pub detail: Option<usize>,
|
pub detail: Option<usize>,
|
||||||
pub selected: bool,
|
pub selected: bool,
|
||||||
|
|
|
@ -294,7 +294,7 @@ pub struct Pane {
|
||||||
toolbar: Entity<Toolbar>,
|
toolbar: Entity<Toolbar>,
|
||||||
pub(crate) workspace: WeakEntity<Workspace>,
|
pub(crate) workspace: WeakEntity<Workspace>,
|
||||||
project: WeakEntity<Project>,
|
project: WeakEntity<Project>,
|
||||||
drag_split_direction: Option<SplitDirection>,
|
pub drag_split_direction: Option<SplitDirection>,
|
||||||
can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool>>,
|
can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool>>,
|
||||||
custom_drop_handle: Option<
|
custom_drop_handle: Option<
|
||||||
Arc<dyn Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>>,
|
Arc<dyn Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>>,
|
||||||
|
@ -309,6 +309,7 @@ pub struct Pane {
|
||||||
&mut Context<Pane>,
|
&mut Context<Pane>,
|
||||||
) -> (Option<AnyElement>, Option<AnyElement>),
|
) -> (Option<AnyElement>, Option<AnyElement>),
|
||||||
>,
|
>,
|
||||||
|
render_tab_bar: Rc<dyn Fn(&mut Pane, &mut Window, &mut Context<Pane>) -> AnyElement>,
|
||||||
show_tab_bar_buttons: bool,
|
show_tab_bar_buttons: bool,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
tab_bar_scroll_handle: ScrollHandle,
|
tab_bar_scroll_handle: ScrollHandle,
|
||||||
|
@ -435,88 +436,8 @@ impl Pane {
|
||||||
custom_drop_handle: None,
|
custom_drop_handle: None,
|
||||||
can_split_predicate: None,
|
can_split_predicate: None,
|
||||||
should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show),
|
should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show),
|
||||||
render_tab_bar_buttons: Rc::new(move |pane, window, cx| {
|
render_tab_bar_buttons: Rc::new(default_render_tab_bar_buttons),
|
||||||
if !pane.has_focus(window, cx) && !pane.context_menu_focused(window, cx) {
|
render_tab_bar: Rc::new(Self::render_tab_bar),
|
||||||
return (None, None);
|
|
||||||
}
|
|
||||||
// Ideally we would return a vec of elements here to pass directly to the [TabBar]'s
|
|
||||||
// `end_slot`, but due to needing a view here that isn't possible.
|
|
||||||
let right_children = h_flex()
|
|
||||||
// Instead we need to replicate the spacing from the [TabBar]'s `end_slot` here.
|
|
||||||
.gap(DynamicSpacing::Base04.rems(cx))
|
|
||||||
.child(
|
|
||||||
PopoverMenu::new("pane-tab-bar-popover-menu")
|
|
||||||
.trigger_with_tooltip(
|
|
||||||
IconButton::new("plus", IconName::Plus).icon_size(IconSize::Small),
|
|
||||||
Tooltip::text("New..."),
|
|
||||||
)
|
|
||||||
.anchor(Corner::TopRight)
|
|
||||||
.with_handle(pane.new_item_context_menu_handle.clone())
|
|
||||||
.menu(move |window, cx| {
|
|
||||||
Some(ContextMenu::build(window, cx, |menu, _, _| {
|
|
||||||
menu.action("New File", NewFile.boxed_clone())
|
|
||||||
.action(
|
|
||||||
"Open File",
|
|
||||||
ToggleFileFinder::default().boxed_clone(),
|
|
||||||
)
|
|
||||||
.separator()
|
|
||||||
.action(
|
|
||||||
"Search Project",
|
|
||||||
DeploySearch {
|
|
||||||
replace_enabled: false,
|
|
||||||
}
|
|
||||||
.boxed_clone(),
|
|
||||||
)
|
|
||||||
.action(
|
|
||||||
"Search Symbols",
|
|
||||||
ToggleProjectSymbols.boxed_clone(),
|
|
||||||
)
|
|
||||||
.separator()
|
|
||||||
.action("New Terminal", NewTerminal.boxed_clone())
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
PopoverMenu::new("pane-tab-bar-split")
|
|
||||||
.trigger_with_tooltip(
|
|
||||||
IconButton::new("split", IconName::Split)
|
|
||||||
.icon_size(IconSize::Small),
|
|
||||||
Tooltip::text("Split Pane"),
|
|
||||||
)
|
|
||||||
.anchor(Corner::TopRight)
|
|
||||||
.with_handle(pane.split_item_context_menu_handle.clone())
|
|
||||||
.menu(move |window, cx| {
|
|
||||||
ContextMenu::build(window, cx, |menu, _, _| {
|
|
||||||
menu.action("Split Right", SplitRight.boxed_clone())
|
|
||||||
.action("Split Left", SplitLeft.boxed_clone())
|
|
||||||
.action("Split Up", SplitUp.boxed_clone())
|
|
||||||
.action("Split Down", SplitDown.boxed_clone())
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child({
|
|
||||||
let zoomed = pane.is_zoomed();
|
|
||||||
IconButton::new("toggle_zoom", IconName::Maximize)
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
.toggle_state(zoomed)
|
|
||||||
.selected_icon(IconName::Minimize)
|
|
||||||
.on_click(cx.listener(|pane, _, window, cx| {
|
|
||||||
pane.toggle_zoom(&crate::ToggleZoom, window, cx);
|
|
||||||
}))
|
|
||||||
.tooltip(move |window, cx| {
|
|
||||||
Tooltip::for_action(
|
|
||||||
if zoomed { "Zoom Out" } else { "Zoom In" },
|
|
||||||
&ToggleZoom,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.into_any_element()
|
|
||||||
.into();
|
|
||||||
(None, right_children)
|
|
||||||
}),
|
|
||||||
show_tab_bar_buttons: TabBarSettings::get_global(cx).show_tab_bar_buttons,
|
show_tab_bar_buttons: TabBarSettings::get_global(cx).show_tab_bar_buttons,
|
||||||
display_nav_history_buttons: Some(
|
display_nav_history_buttons: Some(
|
||||||
TabBarSettings::get_global(cx).show_nav_history_buttons,
|
TabBarSettings::get_global(cx).show_nav_history_buttons,
|
||||||
|
@ -725,6 +646,14 @@ impl Pane {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_render_tab_bar<F>(&mut self, cx: &mut Context<Self>, render: F)
|
||||||
|
where
|
||||||
|
F: 'static + Fn(&mut Pane, &mut Window, &mut Context<Pane>) -> AnyElement,
|
||||||
|
{
|
||||||
|
self.render_tab_bar = Rc::new(render);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut Context<Self>, render: F)
|
pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut Context<Self>, render: F)
|
||||||
where
|
where
|
||||||
F: 'static
|
F: 'static
|
||||||
|
@ -2668,7 +2597,7 @@ impl Pane {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_tab_bar(&mut self, window: &mut Window, cx: &mut Context<Pane>) -> impl IntoElement {
|
fn render_tab_bar(&mut self, window: &mut Window, cx: &mut Context<Pane>) -> AnyElement {
|
||||||
let focus_handle = self.focus_handle.clone();
|
let focus_handle = self.focus_handle.clone();
|
||||||
let navigate_backward = IconButton::new("navigate_backward", IconName::ArrowLeft)
|
let navigate_backward = IconButton::new("navigate_backward", IconName::ArrowLeft)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -2791,6 +2720,7 @@ impl Pane {
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_menu_overlay(menu: &Entity<ContextMenu>) -> Div {
|
pub fn render_menu_overlay(menu: &Entity<ContextMenu>) -> Div {
|
||||||
|
@ -2864,7 +2794,7 @@ impl Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_tab_drop(
|
pub fn handle_tab_drop(
|
||||||
&mut self,
|
&mut self,
|
||||||
dragged_tab: &DraggedTab,
|
dragged_tab: &DraggedTab,
|
||||||
ix: usize,
|
ix: usize,
|
||||||
|
@ -3137,6 +3067,86 @@ impl Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_render_tab_bar_buttons(
|
||||||
|
pane: &mut Pane,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Pane>,
|
||||||
|
) -> (Option<AnyElement>, Option<AnyElement>) {
|
||||||
|
if !pane.has_focus(window, cx) && !pane.context_menu_focused(window, cx) {
|
||||||
|
return (None, None);
|
||||||
|
}
|
||||||
|
// Ideally we would return a vec of elements here to pass directly to the [TabBar]'s
|
||||||
|
// `end_slot`, but due to needing a view here that isn't possible.
|
||||||
|
let right_children = h_flex()
|
||||||
|
// Instead we need to replicate the spacing from the [TabBar]'s `end_slot` here.
|
||||||
|
.gap(DynamicSpacing::Base04.rems(cx))
|
||||||
|
.child(
|
||||||
|
PopoverMenu::new("pane-tab-bar-popover-menu")
|
||||||
|
.trigger_with_tooltip(
|
||||||
|
IconButton::new("plus", IconName::Plus).icon_size(IconSize::Small),
|
||||||
|
Tooltip::text("New..."),
|
||||||
|
)
|
||||||
|
.anchor(Corner::TopRight)
|
||||||
|
.with_handle(pane.new_item_context_menu_handle.clone())
|
||||||
|
.menu(move |window, cx| {
|
||||||
|
Some(ContextMenu::build(window, cx, |menu, _, _| {
|
||||||
|
menu.action("New File", NewFile.boxed_clone())
|
||||||
|
.action("Open File", ToggleFileFinder::default().boxed_clone())
|
||||||
|
.separator()
|
||||||
|
.action(
|
||||||
|
"Search Project",
|
||||||
|
DeploySearch {
|
||||||
|
replace_enabled: false,
|
||||||
|
}
|
||||||
|
.boxed_clone(),
|
||||||
|
)
|
||||||
|
.action("Search Symbols", ToggleProjectSymbols.boxed_clone())
|
||||||
|
.separator()
|
||||||
|
.action("New Terminal", NewTerminal.boxed_clone())
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
PopoverMenu::new("pane-tab-bar-split")
|
||||||
|
.trigger_with_tooltip(
|
||||||
|
IconButton::new("split", IconName::Split).icon_size(IconSize::Small),
|
||||||
|
Tooltip::text("Split Pane"),
|
||||||
|
)
|
||||||
|
.anchor(Corner::TopRight)
|
||||||
|
.with_handle(pane.split_item_context_menu_handle.clone())
|
||||||
|
.menu(move |window, cx| {
|
||||||
|
ContextMenu::build(window, cx, |menu, _, _| {
|
||||||
|
menu.action("Split Right", SplitRight.boxed_clone())
|
||||||
|
.action("Split Left", SplitLeft.boxed_clone())
|
||||||
|
.action("Split Up", SplitUp.boxed_clone())
|
||||||
|
.action("Split Down", SplitDown.boxed_clone())
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child({
|
||||||
|
let zoomed = pane.is_zoomed();
|
||||||
|
IconButton::new("toggle_zoom", IconName::Maximize)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.toggle_state(zoomed)
|
||||||
|
.selected_icon(IconName::Minimize)
|
||||||
|
.on_click(cx.listener(|pane, _, window, cx| {
|
||||||
|
pane.toggle_zoom(&crate::ToggleZoom, window, cx);
|
||||||
|
}))
|
||||||
|
.tooltip(move |window, cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
if zoomed { "Zoom Out" } else { "Zoom In" },
|
||||||
|
&ToggleZoom,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
|
.into();
|
||||||
|
(None, right_children)
|
||||||
|
}
|
||||||
|
|
||||||
impl Focusable for Pane {
|
impl Focusable for Pane {
|
||||||
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
||||||
self.focus_handle.clone()
|
self.focus_handle.clone()
|
||||||
|
@ -3301,7 +3311,7 @@ impl Render for Pane {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.when(self.active_item().is_some() && display_tab_bar, |pane| {
|
.when(self.active_item().is_some() && display_tab_bar, |pane| {
|
||||||
pane.child(self.render_tab_bar(window, cx))
|
pane.child((self.render_tab_bar.clone())(self, window, cx))
|
||||||
})
|
})
|
||||||
.child({
|
.child({
|
||||||
let has_worktrees = project.read(cx).visible_worktrees(cx).next().is_some();
|
let has_worktrees = project.read(cx).visible_worktrees(cx).next().is_some();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue