@@ -26,13 +26,15 @@ use rpc::proto::ViewId;
use settings::Settings;
use stack_frame_list::StackFrameList;
use ui::{
- AnyElement, App, Context, ContextMenu, DropdownMenu, InteractiveElement, IntoElement, Label,
- LabelCommon as _, ParentElement, Render, SharedString, Styled, Window, div, h_flex, v_flex,
+ ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder,
+ InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,
+ StatefulInteractiveElement, Styled, Tab, Window, div, h_flex, v_flex,
};
use util::ResultExt;
use variable_list::VariableList;
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 {
@@ -121,8 +123,9 @@ impl Item for SubView {
cx: &App,
) -> AnyElement {
let label = Label::new(self.tab_name.clone())
+ .size(ui::LabelSize::Small)
.color(params.text_color())
- .into_any_element();
+ .line_height_style(ui::LineHeightStyle::UiLabel);
if !params.selected && self.show_indicator.as_ref()(cx) {
return h_flex()
@@ -133,7 +136,7 @@ impl Item for SubView {
.into_any_element();
}
- label
+ label.into_any_element()
}
}
@@ -266,7 +269,79 @@ fn new_debugger_pane(
})));
pane.display_nav_history_buttons(None);
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(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
});
@@ -294,7 +294,7 @@ pub struct Pane {
toolbar: Entity<Toolbar>,
pub(crate) workspace: WeakEntity<Workspace>,
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>>,
custom_drop_handle: Option<
Arc<dyn Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>>,
@@ -309,6 +309,7 @@ pub struct Pane {
&mut Context<Pane>,
) -> (Option<AnyElement>, Option<AnyElement>),
>,
+ render_tab_bar: Rc<dyn Fn(&mut Pane, &mut Window, &mut Context<Pane>) -> AnyElement>,
show_tab_bar_buttons: bool,
_subscriptions: Vec<Subscription>,
tab_bar_scroll_handle: ScrollHandle,
@@ -435,88 +436,8 @@ impl Pane {
custom_drop_handle: None,
can_split_predicate: None,
should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show),
- render_tab_bar_buttons: Rc::new(move |pane, window, cx| {
- 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)
- }),
+ render_tab_bar_buttons: Rc::new(default_render_tab_bar_buttons),
+ render_tab_bar: Rc::new(Self::render_tab_bar),
show_tab_bar_buttons: TabBarSettings::get_global(cx).show_tab_bar_buttons,
display_nav_history_buttons: Some(
TabBarSettings::get_global(cx).show_nav_history_buttons,
@@ -725,6 +646,14 @@ impl Pane {
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)
where
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 navigate_backward = IconButton::new("navigate_backward", IconName::ArrowLeft)
.icon_size(IconSize::Small)
@@ -2791,6 +2720,7 @@ impl Pane {
})),
),
)
+ .into_any_element()
}
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,
dragged_tab: &DraggedTab,
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 {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
@@ -3301,7 +3311,7 @@ impl Render for 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({
let has_worktrees = project.read(cx).visible_worktrees(cx).next().is_some();