update deps

This commit is contained in:
2026-09-25 09:51:17 +07:00
parent a85b5ee87b
commit 669ca2d57b
7 changed files with 692 additions and 943 deletions
+2
View File
@@ -8,6 +8,8 @@
### Changed
- Migrate the GPUI foundation to the published `gpui-pre` crates and GPUI Kit 0.6, off the zed and gpui-component git pins
### Fixed
### Removed
Generated
+560 -514
View File
File diff suppressed because it is too large Load Diff
+5 -7
View File
@@ -9,15 +9,13 @@ edition = "2024"
publish = false
[workspace.dependencies]
# GPUI
gpui = { git = "https://github.com/zed-industries/zed" }
gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit", "x11", "wayland"] }
gpui_tokio = { git = "https://github.com/zed-industries/zed" }
reqwest_client = { git = "https://github.com/zed-industries/zed" }
gpui = { package = "gpui-pre", version = "0.3.6" }
gpui_platform = { package = "gpui-pre-platform", version = "0.3.6", features = ["font-kit", "x11", "wayland"] }
reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.6" }
# GPUI Kit
gpui-component = { git = "https://github.com/longbridge/gpui-component", rev = "39c2c86dbee7ad445591462f8675f74082e10828", features = ["tree-sitter-languages"], }
gpui-base = { git = "https://github.com/longbridge/gpui-component", rev = "39c2c86dbee7ad445591462f8675f74082e10828" }
gpui-component = { version = "0.6.6", features = ["tree-sitter-languages"] }
gpui-base = "0.6.6"
dock = { path = "crates/dock" }
settings = { path = "crates/settings" }
+2 -30
View File
@@ -11,22 +11,19 @@ use gpui::{
};
use gpui_base::dock::{
DockArea, DockAreaRenderer, DockContext, DockEvent, DockPlacement, NodeId, PanelState,
PanelView, TabGroupRenderer, TilesRenderer,
PanelView, TabGroupRenderer,
};
use gpui_base::resize_handle;
use gpui_component::scroll::ScrollbarMode;
use gpui_component::{ActiveTheme as _, Side};
use crate::invalid_panel::InvalidPanel;
use crate::panel_handle;
use crate::tab_panel::SignedTabGroupSkin;
use crate::tiles::SignedTilesSkin;
use crate::{TAB_BAR_HEIGHT, panel_handle};
/// State the skin shares with its per-container renderers.
pub(crate) struct SkinShared {
area: WeakEntity<DockArea>,
toggle_button_visible: Cell<bool>,
tiles_scrollbar_mode: Cell<Option<ScrollbarMode>>,
/// The dock whose resize handle is being dragged, if any. Only one can be.
resizing_dock: Cell<Option<DockPlacement>>,
}
@@ -40,10 +37,6 @@ impl SkinShared {
self.toggle_button_visible.get()
}
pub(crate) fn tiles_scrollbar_mode(&self) -> Option<ScrollbarMode> {
self.tiles_scrollbar_mode.get()
}
pub(crate) fn resizing_dock(&self) -> &Cell<Option<DockPlacement>> {
&self.resizing_dock
}
@@ -73,7 +66,6 @@ impl SignedDockSkin {
shared: Rc::new(SkinShared {
area: cx.weak_entity(),
toggle_button_visible: Cell::new(true),
tiles_scrollbar_mode: Cell::new(None),
resizing_dock: Cell::new(None),
}),
})
@@ -92,16 +84,6 @@ impl SignedDockSkin {
self.shared.toggle_button_visible.set(visible);
self.shared.notify(cx);
}
/// When a tiles canvas shows its scrollbar. `None` follows the theme.
pub fn tiles_scrollbar_mode(&self) -> Option<ScrollbarMode> {
self.shared.tiles_scrollbar_mode()
}
pub fn set_tiles_scrollbar_mode(&self, mode: Option<ScrollbarMode>, cx: &mut App) {
self.shared.tiles_scrollbar_mode.set(mode);
self.shared.notify(cx);
}
}
/// Payload a dock's resize handle drags.
@@ -154,16 +136,10 @@ impl DockAreaRenderer for SignedDockSkin {
window: &mut Window,
cx: &mut App,
) -> AnyElement {
let placement = dock.placement();
div()
.flex()
.size_full()
.relative()
// A closed bottom dock keeps a strip, and that strip is the tab bar.
.when(!dock.is_open() && placement.is_bottom(), |this| {
this.h(TAB_BAR_HEIGHT)
})
.child(content)
.child(self.render_resize_handle(dock, window, cx))
.child(DockResizeTracker {
@@ -190,10 +166,6 @@ impl DockAreaRenderer for SignedDockSkin {
fn tab_group_renderer(&self) -> Rc<dyn TabGroupRenderer> {
Rc::new(SignedTabGroupSkin::new(self.shared().clone()))
}
fn tiles_renderer(&self) -> Rc<dyn TilesRenderer> {
Rc::new(SignedTilesSkin::new(self.shared().clone()))
}
}
impl SignedDockSkin {
-1
View File
@@ -6,7 +6,6 @@ use gpui_base::dock::PanelView;
mod dock_area;
mod invalid_panel;
mod tab_panel;
mod tiles;
mod window_controls;
pub use dock_area::SignedDockSkin;
+123 -3
View File
@@ -30,6 +30,10 @@ use crate::{TAB_BAR_HEIGHT, t, window_controls};
/// The drag preview's size, reported to base for the drop placeholder.
const DRAG_PREVIEW_SIZE: gpui::Size<gpui::Pixels> = size(px(96.), px(30.));
/// The key a collapsed dock's stand-in tab bar is tagged with, so a test can
/// measure that it fits the strip base reserves. A no-op outside test builds.
const COLLAPSED_TAB_BAR_SELECTOR: &str = "signed-dock-collapsed-tab-bar";
/// A panel's title, or its registered name when the panel has no handle.
pub(crate) fn panel_title(
panel: &Arc<dyn BasePanelView>,
@@ -84,7 +88,6 @@ fn left_top_group(node: &PaneNode) -> Option<NodeId> {
match node.kind() {
PaneRef::Tabs { .. } => Some(node.id()),
PaneRef::Split { children, .. } => children.first().and_then(left_top_group),
PaneRef::Tiles { .. } => None,
}
}
@@ -98,7 +101,6 @@ fn right_top_group(node: &PaneNode) -> Option<NodeId> {
gpui::Axis::Horizontal => children.last(),
}
.and_then(right_top_group),
PaneRef::Tiles { .. } => None,
}
}
@@ -709,7 +711,16 @@ impl TabGroupRenderer for SignedTabGroupSkin {
.child(
Tabs::new("tab-bar")
.px(px(-1.))
.h(TAB_BAR_HEIGHT)
// A collapsed group stands in for a closed bottom dock,
// which base gives only its reserved strip. The bar keeps
// its own height there rather than the title-bar height,
// so it fits instead of being clipped away.
.when(!collapsed, |this| this.h(TAB_BAR_HEIGHT))
// Only a collapsed (bottom) dock tags its bar, so the
// selector is unique when a test measures it.
.when(collapsed, |this| {
this.debug_selector(|| COLLAPSED_TAB_BAR_SELECTOR.to_string())
})
.flex()
.items_center()
.text_color(cx.theme().tab_foreground)
@@ -833,3 +844,112 @@ impl TabGroupRenderer for SignedTabGroupSkin {
)
}
}
#[cfg(test)]
mod tests {
use gpui::{
App, AppContext as _, Context, Empty, EventEmitter, FocusHandle, Focusable, IntoElement,
Render, TestAppContext, Window, px,
};
use gpui_base::dock::{DockArea, DockLayout, DockPlacement, PanelEvent};
use super::COLLAPSED_TAB_BAR_SELECTOR;
use crate::{BasePanel, Panel, SignedDockSkin, panel_handle};
struct Probe {
focus_handle: FocusHandle,
}
impl Probe {
fn new(cx: &mut Context<Self>) -> Self {
Self {
focus_handle: cx.focus_handle(),
}
}
}
impl BasePanel for Probe {
fn panel_name(&self) -> &'static str {
"Probe"
}
}
impl Panel for Probe {
fn title(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
"Probe"
}
}
impl EventEmitter<PanelEvent> for Probe {}
impl Focusable for Probe {
fn focus_handle(&self, _: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Render for Probe {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
Empty
}
}
/// Base reserves only `gpui_base::dock::CLOSED_BOTTOM_STRIP` for a closed
/// bottom dock, so the tab bar drawn in place of one has to shrink to that
/// height; at the full title-bar height its bottom was clipped away.
#[gpui::test]
fn a_collapsed_bottom_dock_bar_fits_the_reserved_strip(cx: &mut TestAppContext) {
cx.update(|cx| {
gpui_component::init(cx);
});
let (area, cx) = cx.add_window_view(|window, cx| {
let skin = SignedDockSkin::new(cx);
DockArea::new("test", None, window, cx).with_renderer(skin)
});
cx.update(|window, cx| {
let center = cx.new(Probe::new);
let bottom = cx.new(Probe::new);
area.update(cx, |area, cx| {
area.set_center(
DockLayout::tabs().panel_view(panel_handle(center), cx),
window,
cx,
);
area.set_dock(
DockPlacement::Bottom,
DockLayout::tabs().panel_view(panel_handle(bottom), cx),
window,
cx,
);
});
});
cx.run_until_parked();
cx.update(|window, cx| window.draw(cx).clear(cx));
// Open, nothing is collapsed, so no bar carries the selector.
assert!(cx.debug_bounds(COLLAPSED_TAB_BAR_SELECTOR).is_none());
cx.update(|window, cx| {
area.update(cx, |area, cx| {
area.toggle_dock(DockPlacement::Bottom, window, cx);
});
});
cx.run_until_parked();
cx.update(|window, cx| window.draw(cx).clear(cx));
let bounds = cx
.debug_bounds(COLLAPSED_TAB_BAR_SELECTOR)
.expect("a collapsed bottom dock draws its stand-in tab bar");
// `gpui_base::dock::CLOSED_BOTTOM_STRIP`, which the engine does not
// re-export. The bar used to be `TAB_BAR_HEIGHT` here and its bottom was
// clipped away; it now has to fit inside what base reserves.
let reserved_strip = px(29.);
assert!(
bounds.size.height <= reserved_strip,
"the collapsed bar is {}px tall, more than the {}px strip base reserves",
bounds.size.height,
reserved_strip,
);
}
}
-388
View File
@@ -1,388 +0,0 @@
use std::rc::Rc;
use gpui::prelude::FluentBuilder as _;
use gpui::{
AnyElement, App, AppContext as _, Context, Div, DragMoveEvent, Empty, InteractiveElement as _,
IntoElement, MouseButton, MouseDownEvent, ParentElement as _, Pixels, Render, ScrollHandle,
Size, Stateful, StatefulInteractiveElement as _, Styled as _, Window, div, px,
};
use gpui_base::dock::{
DRAG_BAR_HEIGHT, HANDLE_SIZE, NodeId, ResizeSide, TileContext, TilesRenderer,
};
use gpui_component::button::{Button, ButtonVariants as _};
use gpui_component::dock::PanelHandle;
use gpui_component::menu::{DropdownMenu as _, PopupMenuItem};
use gpui_component::scroll::Scrollbar;
use gpui_component::{
ActiveTheme as _, Icon, IconName, Selectable as _, Sizable as _, h_flex, v_flex,
};
use crate::dock_area::SkinShared;
use crate::t;
use crate::tab_panel::panel_title;
/// How far a resize handle sticks out past the tile's edge.
const HANDLE_OFFSET: Pixels = px(-4.);
/// The payload a tile drag carries, so one canvas ignores another's drags.
#[derive(Clone)]
struct DragMoving(NodeId);
impl Render for DragMoving {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
Empty
}
}
/// The payload a tile resize carries, for the same reason.
#[derive(Clone)]
struct DragResizing(NodeId);
impl Render for DragResizing {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
Empty
}
}
/// One tiles canvas's appearance.
/// Built once per container, so its scroll position belongs to the canvas it scrolls.
pub(crate) struct SignedTilesSkin {
shared: Rc<SkinShared>,
scroll_handle: ScrollHandle,
}
impl SignedTilesSkin {
pub(crate) fn new(shared: Rc<SkinShared>) -> Self {
Self {
shared,
scroll_handle: ScrollHandle::default(),
}
}
fn resize_handle(
&self,
tile: &TileContext,
id: &'static str,
side: ResizeSide,
build: impl FnOnce(Stateful<Div>) -> Stateful<Div>,
) -> Stateful<Div> {
let node = tile.node();
build(div().id(id).absolute())
.on_mouse_down(MouseButton::Left, {
let tile = tile.clone();
move |event: &MouseDownEvent, window, cx| {
tile.begin_resize(side, event.position, window, cx);
cx.stop_propagation();
}
})
.on_drag(DragResizing(node), |drag, _, _, cx| {
cx.stop_propagation();
cx.new(|_| drag.clone())
})
.on_drag_move({
let tile = tile.clone();
move |event: &DragMoveEvent<DragResizing>, window, cx| {
if event.drag(cx).0 != node {
return;
}
tile.resize_to(event.event.position, window, cx);
}
})
}
/// The trailing controls of a tile's title bar, zoom, close and the ellipsis menu.
/// They use click handlers, the zoom and close actions target a focused tab group.
fn render_tile_controls(
&self,
tile: &TileContext,
window: &mut Window,
cx: &mut App,
) -> impl IntoElement {
let handle = PanelHandle::of(tile.panel());
let control = handle.and_then(|handle| handle.zoom_control(cx));
let zoomed = tile.is_zoomed();
let toolbar_zoom =
tile.is_zoomable() && control.is_some_and(|control| control.toolbar_visible());
let menu_zoom = tile.is_zoomable() && control.is_some_and(|control| control.menu_visible());
let closable = tile.is_closable();
let buttons = handle.and_then(|handle| handle.toolbar_buttons(window, cx));
let panel = handle.map(|handle| handle.panel());
h_flex()
.gap_1()
.flex_shrink_0()
.occlude()
.when_some(buttons, |this, buttons| {
this.children(
buttons
.into_iter()
.map(|button| button.xsmall().ghost().tab_stop(false)),
)
})
.when_some(
match (zoomed, toolbar_zoom) {
(true, _) => Some(("zoom-out", IconName::Minimize, t("Dock.Zoom Out"))),
(false, true) => Some(("zoom-in", IconName::Maximize, t("Dock.Zoom In"))),
(false, false) => None,
},
|this, (id, icon, tooltip)| {
this.child(
Button::new(id)
.icon(icon)
.xsmall()
.ghost()
.tab_stop(false)
.tooltip(tooltip)
.selected(zoomed)
.on_click({
let tile = tile.clone();
move |_, window, cx| tile.toggle_zoom(window, cx)
}),
)
},
)
.child(
Button::new("menu")
.icon(IconName::Ellipsis)
.xsmall()
.ghost()
.tab_stop(false)
.dropdown_menu({
let tile = tile.clone();
move |menu, window, cx| {
menu.when_some(panel.clone(), |menu, panel| {
panel.dropdown_menu(menu, window, cx)
})
.separator()
.item(
PopupMenuItem::new(match zoomed {
true => t("Dock.Zoom Out"),
false => t("Dock.Zoom In"),
})
.disabled(!menu_zoom && !zoomed)
.on_click({
let tile = tile.clone();
move |_, window, cx| tile.toggle_zoom(window, cx)
}),
)
.when(closable, |menu| {
menu.separator()
.item(PopupMenuItem::new(t("Dock.Close")).on_click({
let tile = tile.clone();
move |_, window, cx| tile.close(window, cx)
}))
})
}
})
.anchor(gpui::Anchor::TopRight),
)
}
}
impl TilesRenderer for SignedTilesSkin {
fn frame(&self, _: &mut Window, cx: &mut App) -> Stateful<Div> {
div()
.id("tiles")
.relative()
.size_full()
.bg(cx.theme().tokens.tiles)
.track_scroll(&self.scroll_handle)
.overflow_scroll()
}
fn tile_frame(&self, tile: &TileContext, _: &mut Window, cx: &mut App) -> Stateful<Div> {
v_flex()
.id(("tile", tile.panel_id().as_u64()))
.occlude()
.overflow_hidden()
.bg(cx.theme().tokens.background)
.border_1()
.border_color(cx.theme().border)
.rounded(cx.theme().tile_radius)
// Room for the title bar, which overlays the top padding.
// Base draws the panel as a plain child, this keeps them apart.
.pt(DRAG_BAR_HEIGHT)
// Base stores no bounds on a zoomed tile, the skin decides how it fills the dock.
.when(tile.is_zoomed(), |this| this.size_full())
.on_mouse_down(MouseButton::Left, {
let tile = tile.clone();
move |_, window, cx| tile.bring_to_front(window, cx)
})
// A gesture can end anywhere, so both mouse-up hooks run.
// Each is a no-op unless this tile is the one that moved.
.on_mouse_up(MouseButton::Left, {
let tile = tile.clone();
move |_, window, cx| {
tile.end_move(window, cx);
tile.end_resize(window, cx);
}
})
.on_mouse_up_out(MouseButton::Left, {
let tile = tile.clone();
move |_, window, cx| {
tile.end_move(window, cx);
tile.end_resize(window, cx);
}
})
}
fn render_drag_bar(&self, tile: &TileContext, window: &mut Window, cx: &mut App) -> AnyElement {
let node = tile.node();
let handle = PanelHandle::of(tile.panel());
let title_style = handle.and_then(|handle| handle.title_style(cx));
h_flex()
.id("drag-bar")
.absolute()
.top_0()
.left_0()
.w_full()
.h(DRAG_BAR_HEIGHT)
.items_center()
.gap_1()
.pl_3()
.pr_2()
.when_some(title_style, |this, style| {
this.bg(style.background).text_color(style.foreground)
})
.child(
div()
.flex_1()
.min_w_16()
.overflow_hidden()
.text_ellipsis()
.whitespace_nowrap()
.child(panel_title(tile.panel(), window, cx)),
)
.children(handle.and_then(|handle| handle.title_suffix(window, cx)))
.child(self.render_tile_controls(tile, window, cx))
// A zoomed tile is not at its stored bounds, so moving it would mean nothing.
.when(!tile.is_zoomed(), |this| {
this.cursor_grab()
.on_mouse_down(MouseButton::Left, {
let tile = tile.clone();
move |event: &MouseDownEvent, window, cx| {
tile.begin_move(event.position, window, cx);
}
})
.on_drag(DragMoving(node), |drag, _, _, cx| {
cx.stop_propagation();
cx.new(|_| drag.clone())
})
.on_drag_move({
let tile = tile.clone();
move |event: &DragMoveEvent<DragMoving>, window, cx| {
if event.drag(cx).0 != node {
return;
}
tile.move_to(event.event.position, window, cx);
}
})
})
.into_any_element()
}
fn render_resize_handles(
&self,
tile: &TileContext,
_: &mut Window,
cx: &mut App,
) -> AnyElement {
let bounds = tile.bounds();
// A passive full-tile box, so handles sit against the tile, not its flow neighbours.
// It registers no interaction, so it does not shadow the panel underneath.
div()
.absolute()
.top_0()
.left_0()
.size_full()
.child(
self.resize_handle(tile, "left-resize-handle", ResizeSide::Left, |this| {
this.cursor_ew_resize()
.top_0()
.left(HANDLE_OFFSET)
.w(HANDLE_SIZE)
.h(bounds.size.height)
}),
)
.child(
self.resize_handle(tile, "right-resize-handle", ResizeSide::Right, |this| {
this.cursor_ew_resize()
.top_0()
.right(HANDLE_OFFSET)
.w(HANDLE_SIZE)
.h(bounds.size.height)
}),
)
.child(
self.resize_handle(tile, "top-resize-handle", ResizeSide::Top, |this| {
this.cursor_ns_resize()
.left_0()
.top(HANDLE_OFFSET)
.w(bounds.size.width)
.h(HANDLE_SIZE)
}),
)
.child(
self.resize_handle(tile, "bottom-resize-handle", ResizeSide::Bottom, |this| {
this.cursor_ns_resize()
.left_0()
.bottom(HANDLE_OFFSET)
.w(bounds.size.width)
.h(HANDLE_SIZE)
}),
)
.child(
Icon::new(IconName::ResizeCorner)
.size_3()
.absolute()
.right(px(1.))
.bottom(px(1.))
.text_color(cx.theme().muted_foreground.opacity(0.5)),
)
.child(self.resize_handle(
tile,
"corner-resize-handle",
ResizeSide::BottomRight,
|this| {
this.cursor_nwse_resize()
.right(HANDLE_OFFSET)
.bottom(HANDLE_OFFSET)
.size_3()
},
))
.into_any_element()
}
/// Gives the tile's panel `size_full`, base draws it as a plain child otherwise.
fn panel_frame(&self, tile: &TileContext, _: &mut Window, _: &mut App) -> Stateful<Div> {
h_flex()
.id(("tile-panel", tile.panel_id().as_u64()))
.overflow_hidden()
.size_full()
}
/// The canvas scrollbar, as an overlay.
/// Placed inside the frame it would end up underneath every tile.
fn render_overlay(
&self,
content: Size<Pixels>,
_: &mut Window,
_: &mut App,
) -> Option<AnyElement> {
Some(
Scrollbar::new(&self.scroll_handle)
.scroll_size(content)
.when_some(self.shared.tiles_scrollbar_mode(), |this, mode| {
this.mode(mode)
})
.into_any_element(),
)
}
fn grid_size(&self, cx: &App) -> Pixels {
cx.theme().tile_grid_size
}
}