Allow splitting the terminal panel (#21238)

Closes https://github.com/zed-industries/zed/issues/4351


![it_splits](https://github.com/user-attachments/assets/40de03c9-2173-4441-ba96-8e91537956e0)

Applies the same splitting mechanism, as Zed's central pane has, to the
terminal panel.
Similar navigation, splitting and (de)serialization capabilities are
supported.

Notable caveats:
* zooming keeps the terminal splits' ratio, rather expanding the
terminal pane
* on macOs, central panel is split with `cmd-k up/down/etc.` but `cmd-k`
is a "standard" terminal clearing keybinding on macOS, so terminal panel
splitting is done via `ctrl-k up/down/etc.`
* task terminals are "split" into regular terminals, and also not
persisted (same as currently in the terminal)

Seems ok for the initial version, we can revisit and polish things
later.

Release Notes:

- Added the ability to split the terminal panel
This commit is contained in:
Kirill Bulatov 2024-11-27 20:22:39 +02:00 committed by GitHub
parent 4564da2875
commit d0bafce86b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 953 additions and 348 deletions

View file

@ -777,7 +777,7 @@ pub struct ViewId {
pub id: u64,
}
struct FollowerState {
pub struct FollowerState {
center_pane: View<Pane>,
dock_pane: Option<View<Pane>>,
active_view_id: Option<ViewId>,
@ -887,14 +887,16 @@ impl Workspace {
let pane_history_timestamp = Arc::new(AtomicUsize::new(0));
let center_pane = cx.new_view(|cx| {
Pane::new(
let mut center_pane = Pane::new(
weak_handle.clone(),
project.clone(),
pane_history_timestamp.clone(),
None,
NewFile.boxed_clone(),
cx,
)
);
center_pane.set_can_split(Some(Arc::new(|_, _, _| true)));
center_pane
});
cx.subscribe(&center_pane, Self::handle_pane_event).detach();
@ -2464,14 +2466,16 @@ impl Workspace {
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> View<Pane> {
let pane = cx.new_view(|cx| {
Pane::new(
let mut pane = Pane::new(
self.weak_handle(),
self.project.clone(),
self.pane_history_timestamp.clone(),
None,
NewFile.boxed_clone(),
cx,
)
);
pane.set_can_split(Some(Arc::new(|_, _, _| true)));
pane
});
cx.subscribe(&pane, Self::handle_pane_event).detach();
self.panes.push(pane.clone());
@ -2955,30 +2959,9 @@ impl Workspace {
direction: SplitDirection,
cx: &WindowContext,
) -> Option<View<Pane>> {
let bounding_box = self.center.bounding_box_for_pane(&self.active_pane)?;
let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx);
let center = match cursor {
Some(cursor) if bounding_box.contains(&cursor) => cursor,
_ => bounding_box.center(),
};
let distance_to_next = pane_group::HANDLE_HITBOX_SIZE;
let target = match direction {
SplitDirection::Left => {
Point::new(bounding_box.left() - distance_to_next.into(), center.y)
}
SplitDirection::Right => {
Point::new(bounding_box.right() + distance_to_next.into(), center.y)
}
SplitDirection::Up => {
Point::new(center.x, bounding_box.top() - distance_to_next.into())
}
SplitDirection::Down => {
Point::new(center.x, bounding_box.bottom() + distance_to_next.into())
}
};
self.center.pane_at_pixel_position(target).cloned()
self.center
.find_pane_in_direction(&self.active_pane, direction, cx)
.cloned()
}
pub fn swap_pane_in_direction(
@ -4591,6 +4574,10 @@ impl Workspace {
let window = cx.window_handle().downcast::<Workspace>()?;
cx.read_window(&window, |workspace, _| workspace).ok()
}
pub fn zoomed_item(&self) -> Option<&AnyWeakView> {
self.zoomed.as_ref()
}
}
fn leader_border_for_pane(