From eb04f74d19052bc60aeb0d40617ccdd1fe7bb016 Mon Sep 17 00:00:00 2001 From: kuddev Date: Fri, 21 Aug 2026 22:09:24 +0800 Subject: [PATCH] fix(gpui): consolidate top tab actions --- nebula_app/src/gpui_shell/workspace.rs | 28 ++++----- .../src/gpui_shell/workspace/top_tabs.rs | 61 +++++++++++++------ 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/nebula_app/src/gpui_shell/workspace.rs b/nebula_app/src/gpui_shell/workspace.rs index 70dc63a..ac33b47 100644 --- a/nebula_app/src/gpui_shell/workspace.rs +++ b/nebula_app/src/gpui_shell/workspace.rs @@ -4147,23 +4147,23 @@ impl Render for NebulaWorkspace { .inset_0(), ) .child( - // 标题栏左上与旧壳同构:侧栏开关 + 齿轮,两枚裸图标,无应用名 - // 文案。图标用静态 PanelLeft(旧壳折叠/展开共用同一枚方块标)。 - // - // 组件库默认 TitleBar 只有 34px,而普通 Button 的命中块是 - // 32px,上下各剩 1px,视觉上就会紧贴窗口顶边。旧壳合同是 - // 8px 外边距 + 40px 标题带,总高 48px;显式覆写后按钮垂直 - // 居中,上下各留 8px,且右侧窗口控制仍共享同一标题带。 + // 侧栏模式保留旧壳的侧栏开关与齿轮。组件默认 34px 标题栏会让 + // 32px 按钮几乎贴边,因此显式设为 48px、上下各留 8px;右侧 + // 窗口控制仍共享同一标题带。 TitleBar::new() .h(px(48.0)) .when(top_tabs, |bar| { - bar.child(self.render_top_title_bar( - files_active, - git_active, - settings_active, - window, - cx, - )) + // 正文卡在顶部模式保留 8px 左缝;标题栏默认 12px, + // 这里覆写后首个 tab 才与卡内 powerline 严格同轴。 + bar.pl(px(top_tabs::TOP_TAB_LEFT_INSET)).child( + self.render_top_title_bar( + files_active, + git_active, + settings_active, + window, + cx, + ), + ) }) .when(!top_tabs, |bar| { bar.child( diff --git a/nebula_app/src/gpui_shell/workspace/top_tabs.rs b/nebula_app/src/gpui_shell/workspace/top_tabs.rs index 44a0b8f..bb49e07 100644 --- a/nebula_app/src/gpui_shell/workspace/top_tabs.rs +++ b/nebula_app/src/gpui_shell/workspace/top_tabs.rs @@ -12,22 +12,26 @@ use gpui::{ ScrollWheelEvent, SharedString, StatefulInteractiveElement as _, Styled as _, StyledImage as _, Window, div, ease_out_quint, img, px, }; +use gpui_component::menu::PopupMenuItem; use crate::gpui_shell::prelude::*; use crate::gpui_shell::terminal::view::SidebarActivity; use super::{ - NebulaWorkspace, TAB_LABEL_ICON_SIZE, TAB_LABEL_ICON_W, TabDrag, TabDragAxis, TabPresentation, + NebulaWorkspace, OpenSettings, TAB_LABEL_ICON_SIZE, TAB_LABEL_ICON_W, TabDrag, TabDragAxis, + TabPresentation, ToggleShellPicker, }; pub(super) const TOP_TAB_H: f32 = 34.0; +/// 与顶部模式正文卡片的 `px_2` 左边距同源,让首个 tab 和终端左缘对齐。 +pub(super) const TOP_TAB_LEFT_INSET: f32 = 8.0; const TOP_TAB_MIN_W: f32 = 120.0; const TOP_TAB_MAX_W: f32 = 220.0; const TOP_TAB_GAP: f32 = 4.0; const TOP_TAB_STATUS_W: f32 = 28.0; -/// 标题栏中不属于 tab 视口的固定预算:左内边距、五枚 32px 操作按钮、 +/// 标题栏中不属于 tab 视口的固定预算:左内边距、四枚 32px 操作按钮、 /// 三枚 34px 窗口按钮,以及至少 72px 的可拖拽空白。 -const TOP_TAB_RESERVED_W: f32 = 12.0 + 32.0 * 5.0 + 34.0 * 3.0 + 72.0; +const TOP_TAB_RESERVED_W: f32 = TOP_TAB_LEFT_INSET + 32.0 * 4.0 + 34.0 * 3.0 + 72.0; fn tab_width(viewport_w: f32, count: usize) -> f32 { if count == 0 || viewport_w <= 1.0 { @@ -361,20 +365,12 @@ impl NebulaWorkspace { }); } + let menu_workspace = cx.entity().downgrade(); + h_flex() .size_full() .min_w_0() .items_center() - .child( - Button::new("top-open-settings") - .icon(IconName::Settings) - .ghost() - .selected(settings_active) - .tooltip("设置 (Ctrl+,)") - .on_click(cx.listener(|this, _, window, cx| { - this.open_settings(window, cx); - })), - ) .child( // Windows Terminal 的 TabView 贴住标题栏底边;只让 tab 与其 // 相邻操作按钮下沉,标题栏两侧的独立工具仍保持垂直居中。 @@ -421,10 +417,41 @@ impl NebulaWorkspace { Button::new("top-tabs-menu") .icon(IconName::EllipsisVertical) .ghost() - .tooltip("选择终端 (Ctrl+K)") - .on_click(cx.listener(|this, _, window, cx| { - this.open_shell_palette(window, cx); - })), + .selected(settings_active) + .tooltip("更多") + .dropdown_menu_with_anchor( + gpui::Corner::TopRight, + move |menu, _, _| { + let shell_picker = menu_workspace.clone(); + let settings = menu_workspace.clone(); + menu.external_link_icon(false) + .item( + PopupMenuItem::new("选择终端") + .icon(IconName::SquareTerminal) + .action(Box::new(ToggleShellPicker)) + .on_click(move |_, window, cx| { + if let Some(workspace) = shell_picker.upgrade() { + workspace.update(cx, |this, cx| { + this.open_shell_palette(window, cx); + }); + } + }), + ) + .separator() + .item( + PopupMenuItem::new("设置") + .icon(IconName::Settings) + .action(Box::new(OpenSettings)) + .on_click(move |_, window, cx| { + if let Some(workspace) = settings.upgrade() { + workspace.update(cx, |this, cx| { + this.open_settings(window, cx); + }); + } + }), + ) + }, + ), ), ) // Windows Terminal 的新建 split button 紧跟 TabView;剩余空间