diff --git a/crates/terminal_view/src/view/render_layout.rs b/crates/terminal_view/src/view/render_layout.rs index e9537b058..10738e0a0 100644 --- a/crates/terminal_view/src/view/render_layout.rs +++ b/crates/terminal_view/src/view/render_layout.rs @@ -88,6 +88,7 @@ impl TerminalView { ) -> AnyElement { let state = self.tool_dock_render_state(cx); let view = cx.entity().clone(); + let bounds_view = view.clone(); let bg_color = self.current_theme.background; let center = CenterRegionState { font_family, @@ -106,6 +107,13 @@ impl TerminalView { .min_w_0() .min_h_0() .overflow_hidden() + // 拖动换算要用 dock 根元素的真实 bounds;事件监听元素自身是 0×0, + // 而 window.bounds() 在嵌入面板里也不等于 dock 的位置。 + .on_prepaint(move |bounds, _, cx| { + bounds_view.update(cx, |view, _| { + view.view_bounds = bounds; + }); + }) .bg(bg_color) .key_context(TERMINAL_CONTEXT) .on_action(cx.listener(Self::start_recording_action)) diff --git a/crates/terminal_view/src/view/resize_event_handler.rs b/crates/terminal_view/src/view/resize_event_handler.rs index 14439cc45..8cb80d385 100644 --- a/crates/terminal_view/src/view/resize_event_handler.rs +++ b/crates/terminal_view/src/view/resize_event_handler.rs @@ -40,16 +40,11 @@ impl Element for ResizeEventHandler { _: Option<&InspectorElementId>, _: Bounds, _: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, + _window: &mut Window, + _cx: &mut App, ) -> Self::PrepaintState { - let bounds = window.bounds(); - self.view.update(cx, |view, _| { - view.view_bounds = Bounds { - origin: Point::default(), - size: bounds.size, - }; - }); + // view_bounds 由 dock 根元素的 on_prepaint 写入:本元素是 0×0 的弹性 + // 子项,而窗口 bounds 在嵌入面板里也不等于 dock 的位置。 } fn paint( diff --git a/crates/terminal_view/src/view/tests/layout.rs b/crates/terminal_view/src/view/tests/layout.rs index 2ddf757b8..f9d283847 100644 --- a/crates/terminal_view/src/view/tests/layout.rs +++ b/crates/terminal_view/src/view/tests/layout.rs @@ -945,3 +945,24 @@ fn terminal_render_uses_cached_font_metrics() { assert!(metric_source.contains("fn refresh_terminal_font_metrics(")); assert!(!render_setup.contains("cx.text_system().all_font_names()")); } + +/// 内嵌 dock 的拖动换算同样必须使用 dock 根元素的真实 bounds, +/// 不能用 `window.bounds()`(嵌入面板时原点/尺寸都不是 dock 的)。 +#[test] +fn embedded_sidebar_resize_uses_dock_bounds_not_window_bounds() { + let render_layout = include_str!("../render_layout.rs"); + let resize_event_handler = include_str!("../resize_event_handler.rs"); + + assert!( + render_layout.contains(".on_prepaint(move |bounds, _, cx|"), + "tool dock root must capture its own bounds via on_prepaint" + ); + assert!( + render_layout.contains("view.view_bounds = bounds"), + "tool dock root prepaint must write view_bounds" + ); + assert!( + !resize_event_handler.contains("window.bounds()"), + "resize event handler must not derive view bounds from the window" + ); +} diff --git a/crates/terminal_view/src/workspace/layout.rs b/crates/terminal_view/src/workspace/layout.rs index 14d9b9557..b84717827 100644 --- a/crates/terminal_view/src/workspace/layout.rs +++ b/crates/terminal_view/src/workspace/layout.rs @@ -3,7 +3,7 @@ use gpui::{ AnyElement, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Styled as _, div, }; -use gpui_component::{h_flex, v_flex}; +use gpui_component::{ElementExt as _, h_flex, v_flex}; use one_core::layout::TOOLBAR_WIDTH; use one_core::sidebar_contribution::SidebarPlacement; @@ -76,12 +76,21 @@ impl TerminalWorkspace { ) }); + let workspace = cx.entity(); h_flex() .id("terminal-workspace-root") .size_full() .min_w_0() .min_h_0() .overflow_hidden() + // 侧栏拖动换算要用工作区根元素的真实 bounds。事件监听元素本身是 + // 0×0 的弹性子项,拿它自己的 bounds 当工作区边界会让左/下拖动 + // 永远被夹到最小宽度。 + .on_prepaint(move |bounds, _, cx| { + workspace.update(cx, |workspace, _| { + workspace.workspace_bounds = bounds; + }); + }) .when_some(left, |this, panel| { this.child( div() diff --git a/crates/terminal_view/src/workspace/resize.rs b/crates/terminal_view/src/workspace/resize.rs index ea8787dc0..524797be5 100644 --- a/crates/terminal_view/src/workspace/resize.rs +++ b/crates/terminal_view/src/workspace/resize.rs @@ -116,14 +116,13 @@ impl Element for WorkspaceResizeEventHandler { &mut self, _: Option<&GlobalElementId>, _: Option<&InspectorElementId>, - bounds: Bounds, + _: Bounds, _: &mut Self::RequestLayoutState, _: &mut Window, - cx: &mut App, + _: &mut App, ) -> Self::PrepaintState { - self.workspace.update(cx, |workspace, _| { - workspace.workspace_bounds = bounds; - }); + // 工作区 bounds 由根元素的 on_prepaint 写入:本元素是 0×0 的弹性 + // 子项,自身 bounds 不能当作工作区边界。 } fn paint( diff --git a/crates/terminal_view/src/workspace/tests.rs b/crates/terminal_view/src/workspace/tests.rs index a937fa9cd..c086bbb76 100644 --- a/crates/terminal_view/src/workspace/tests.rs +++ b/crates/terminal_view/src/workspace/tests.rs @@ -198,3 +198,25 @@ fn closing_pane_preserves_surviving_ratio(cx: &mut TestAppContext) { assert_width(pane_width(visual, PANE_SELECTORS[1]), px(630.)); assert_eq!(2, visual.read(|cx| harness.read(cx).tree.panes().len())); } + +/// 侧栏拖动换算依赖 `workspace_bounds`。它必须来自工作区根元素的真实 +/// bounds:事件监听元素是 0×0 的弹性子项,拿它自己的 bounds 会让左侧 +/// 与底部拖动永远被夹到最小宽度(左侧面板拖不动)。 +#[test] +fn sidebar_resize_bounds_come_from_the_workspace_root() { + let layout_source = include_str!("layout.rs"); + let resize_source = include_str!("resize.rs"); + + assert!( + layout_source.contains(".on_prepaint(move |bounds, _, cx|"), + "workspace root must capture its own bounds via on_prepaint" + ); + assert!( + layout_source.contains("workspace.workspace_bounds = bounds"), + "workspace root prepaint must write workspace_bounds" + ); + assert!( + !resize_source.contains("workspace_bounds = bounds"), + "resize event handler is zero-sized; its bounds must not be used as the workspace bounds" + ); +}