update deps
This commit is contained in:
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
### Changed
|
### 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
|
### Fixed
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|||||||
Generated
+560
-514
File diff suppressed because it is too large
Load Diff
+5
-7
@@ -9,15 +9,13 @@ edition = "2024"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
# GPUI
|
gpui = { package = "gpui-pre", version = "0.3.6" }
|
||||||
gpui = { git = "https://github.com/zed-industries/zed" }
|
gpui_platform = { package = "gpui-pre-platform", version = "0.3.6", features = ["font-kit", "x11", "wayland"] }
|
||||||
gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit", "x11", "wayland"] }
|
reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.6" }
|
||||||
gpui_tokio = { git = "https://github.com/zed-industries/zed" }
|
|
||||||
reqwest_client = { git = "https://github.com/zed-industries/zed" }
|
|
||||||
|
|
||||||
# GPUI Kit
|
# GPUI Kit
|
||||||
gpui-component = { git = "https://github.com/longbridge/gpui-component", rev = "39c2c86dbee7ad445591462f8675f74082e10828", features = ["tree-sitter-languages"], }
|
gpui-component = { version = "0.6.6", features = ["tree-sitter-languages"] }
|
||||||
gpui-base = { git = "https://github.com/longbridge/gpui-component", rev = "39c2c86dbee7ad445591462f8675f74082e10828" }
|
gpui-base = "0.6.6"
|
||||||
|
|
||||||
dock = { path = "crates/dock" }
|
dock = { path = "crates/dock" }
|
||||||
settings = { path = "crates/settings" }
|
settings = { path = "crates/settings" }
|
||||||
|
|||||||
@@ -11,22 +11,19 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use gpui_base::dock::{
|
use gpui_base::dock::{
|
||||||
DockArea, DockAreaRenderer, DockContext, DockEvent, DockPlacement, NodeId, PanelState,
|
DockArea, DockAreaRenderer, DockContext, DockEvent, DockPlacement, NodeId, PanelState,
|
||||||
PanelView, TabGroupRenderer, TilesRenderer,
|
PanelView, TabGroupRenderer,
|
||||||
};
|
};
|
||||||
use gpui_base::resize_handle;
|
use gpui_base::resize_handle;
|
||||||
use gpui_component::scroll::ScrollbarMode;
|
|
||||||
use gpui_component::{ActiveTheme as _, Side};
|
use gpui_component::{ActiveTheme as _, Side};
|
||||||
|
|
||||||
use crate::invalid_panel::InvalidPanel;
|
use crate::invalid_panel::InvalidPanel;
|
||||||
|
use crate::panel_handle;
|
||||||
use crate::tab_panel::SignedTabGroupSkin;
|
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.
|
/// State the skin shares with its per-container renderers.
|
||||||
pub(crate) struct SkinShared {
|
pub(crate) struct SkinShared {
|
||||||
area: WeakEntity<DockArea>,
|
area: WeakEntity<DockArea>,
|
||||||
toggle_button_visible: Cell<bool>,
|
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.
|
/// The dock whose resize handle is being dragged, if any. Only one can be.
|
||||||
resizing_dock: Cell<Option<DockPlacement>>,
|
resizing_dock: Cell<Option<DockPlacement>>,
|
||||||
}
|
}
|
||||||
@@ -40,10 +37,6 @@ impl SkinShared {
|
|||||||
self.toggle_button_visible.get()
|
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>> {
|
pub(crate) fn resizing_dock(&self) -> &Cell<Option<DockPlacement>> {
|
||||||
&self.resizing_dock
|
&self.resizing_dock
|
||||||
}
|
}
|
||||||
@@ -73,7 +66,6 @@ impl SignedDockSkin {
|
|||||||
shared: Rc::new(SkinShared {
|
shared: Rc::new(SkinShared {
|
||||||
area: cx.weak_entity(),
|
area: cx.weak_entity(),
|
||||||
toggle_button_visible: Cell::new(true),
|
toggle_button_visible: Cell::new(true),
|
||||||
tiles_scrollbar_mode: Cell::new(None),
|
|
||||||
resizing_dock: Cell::new(None),
|
resizing_dock: Cell::new(None),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -92,16 +84,6 @@ impl SignedDockSkin {
|
|||||||
self.shared.toggle_button_visible.set(visible);
|
self.shared.toggle_button_visible.set(visible);
|
||||||
self.shared.notify(cx);
|
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.
|
/// Payload a dock's resize handle drags.
|
||||||
@@ -154,16 +136,10 @@ impl DockAreaRenderer for SignedDockSkin {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let placement = dock.placement();
|
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.relative()
|
.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(content)
|
||||||
.child(self.render_resize_handle(dock, window, cx))
|
.child(self.render_resize_handle(dock, window, cx))
|
||||||
.child(DockResizeTracker {
|
.child(DockResizeTracker {
|
||||||
@@ -190,10 +166,6 @@ impl DockAreaRenderer for SignedDockSkin {
|
|||||||
fn tab_group_renderer(&self) -> Rc<dyn TabGroupRenderer> {
|
fn tab_group_renderer(&self) -> Rc<dyn TabGroupRenderer> {
|
||||||
Rc::new(SignedTabGroupSkin::new(self.shared().clone()))
|
Rc::new(SignedTabGroupSkin::new(self.shared().clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tiles_renderer(&self) -> Rc<dyn TilesRenderer> {
|
|
||||||
Rc::new(SignedTilesSkin::new(self.shared().clone()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignedDockSkin {
|
impl SignedDockSkin {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use gpui_base::dock::PanelView;
|
|||||||
mod dock_area;
|
mod dock_area;
|
||||||
mod invalid_panel;
|
mod invalid_panel;
|
||||||
mod tab_panel;
|
mod tab_panel;
|
||||||
mod tiles;
|
|
||||||
mod window_controls;
|
mod window_controls;
|
||||||
|
|
||||||
pub use dock_area::SignedDockSkin;
|
pub use dock_area::SignedDockSkin;
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ use crate::{TAB_BAR_HEIGHT, t, window_controls};
|
|||||||
/// The drag preview's size, reported to base for the drop placeholder.
|
/// 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.));
|
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.
|
/// A panel's title, or its registered name when the panel has no handle.
|
||||||
pub(crate) fn panel_title(
|
pub(crate) fn panel_title(
|
||||||
panel: &Arc<dyn BasePanelView>,
|
panel: &Arc<dyn BasePanelView>,
|
||||||
@@ -84,7 +88,6 @@ fn left_top_group(node: &PaneNode) -> Option<NodeId> {
|
|||||||
match node.kind() {
|
match node.kind() {
|
||||||
PaneRef::Tabs { .. } => Some(node.id()),
|
PaneRef::Tabs { .. } => Some(node.id()),
|
||||||
PaneRef::Split { children, .. } => children.first().and_then(left_top_group),
|
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(),
|
gpui::Axis::Horizontal => children.last(),
|
||||||
}
|
}
|
||||||
.and_then(right_top_group),
|
.and_then(right_top_group),
|
||||||
PaneRef::Tiles { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +711,16 @@ impl TabGroupRenderer for SignedTabGroupSkin {
|
|||||||
.child(
|
.child(
|
||||||
Tabs::new("tab-bar")
|
Tabs::new("tab-bar")
|
||||||
.px(px(-1.))
|
.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()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.text_color(cx.theme().tab_foreground)
|
.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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user