Add support for resizing panes using vim motions (#21038)

Closes #8628

Release Notes:

- Added support for resizing the current pane using vim keybinds with
the intention to follow the functionality of vim
  - "ctrl-w +" to make a pane taller 
  - "ctrl-w -" to make the pane shorter
  - "ctrl-w >" to make a pane wider
  - "ctrl-w <" to make the pane narrower
- Changed vim pre_count and post_count to globals to allow for other
crates to use the vim count. In this case, it allows for resizing by
more than one unit. For example, "10 ctrl-w -" will decrease the height
of the pane 10 times more than "ctrl-w -"
- This pr does **not** add keybinds for making all panes in an axis
equal size and does **not** add support for resizing docks. This is
mentioned because these could be implied by the original issue

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
AidanV 2024-11-26 16:24:29 -08:00 committed by GitHub
parent d75d34576a
commit f702575255
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 251 additions and 68 deletions

View file

@ -8,8 +8,8 @@ use call::{ActiveCall, ParticipantLocation};
use client::proto::PeerId;
use collections::HashMap;
use gpui::{
point, size, AnyView, AnyWeakView, Axis, Bounds, IntoElement, Model, MouseButton, Pixels,
Point, StyleRefinement, View, ViewContext,
point, size, Along, AnyView, AnyWeakView, Axis, Bounds, IntoElement, Model, MouseButton,
Pixels, Point, StyleRefinement, View, ViewContext,
};
use parking_lot::Mutex;
use project::Project;
@ -90,6 +90,21 @@ impl PaneGroup {
}
}
pub fn resize(
&mut self,
pane: &View<Pane>,
direction: Axis,
amount: Pixels,
bounds: &Bounds<Pixels>,
) {
match &mut self.root {
Member::Pane(_) => {}
Member::Axis(axis) => {
let _ = axis.resize(pane, direction, amount, bounds);
}
};
}
pub fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
match &mut self.root {
Member::Pane(_) => {}
@ -445,6 +460,116 @@ impl PaneAxis {
}
}
fn resize(
&mut self,
pane: &View<Pane>,
axis: Axis,
amount: Pixels,
bounds: &Bounds<Pixels>,
) -> Option<bool> {
let container_size = self
.bounding_boxes
.lock()
.iter()
.filter_map(|e| *e)
.reduce(|acc, e| acc.union(&e))
.unwrap_or(*bounds)
.size;
let found_pane = self
.members
.iter()
.any(|member| matches!(member, Member::Pane(p) if p == pane));
if found_pane && self.axis != axis {
return Some(false); // pane found but this is not the correct axis direction
}
let mut found_axis_index: Option<usize> = None;
if !found_pane {
for (i, pa) in self.members.iter_mut().enumerate() {
if let Member::Axis(pa) = pa {
if let Some(done) = pa.resize(pane, axis, amount, bounds) {
if done {
return Some(true); // pane found and operations already done
} else if self.axis != axis {
return Some(false); // pane found but this is not the correct axis direction
} else {
found_axis_index = Some(i); // pane found and this is correct direction
}
}
}
}
found_axis_index?; // no pane found
}
let min_size = match axis {
Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
Axis::Vertical => px(VERTICAL_MIN_SIZE),
};
let mut flexes = self.flexes.lock();
let ix = if found_pane {
self.members.iter().position(|m| {
if let Member::Pane(p) = m {
p == pane
} else {
false
}
})
} else {
found_axis_index
};
if ix.is_none() {
return Some(true);
}
let ix = ix.unwrap_or(0);
let size = move |ix, flexes: &[f32]| {
container_size.along(axis) * (flexes[ix] / flexes.len() as f32)
};
// Don't allow resizing to less than the minimum size, if elements are already too small
if min_size - px(1.) > size(ix, flexes.as_slice()) {
return Some(true);
}
let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
let flex_change = flexes.len() as f32 * pixel_dx / container_size.along(axis);
let current_target_flex = flexes[target_ix] + flex_change;
let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
(current_target_flex, next_target_flex)
};
let apply_changes =
|current_ix: usize, proposed_current_pixel_change: Pixels, flexes: &mut [f32]| {
let next_target_size = Pixels::max(
size(current_ix + 1, flexes) - proposed_current_pixel_change,
min_size,
);
let current_target_size = Pixels::max(
size(current_ix, flexes) + size(current_ix + 1, flexes) - next_target_size,
min_size,
);
let current_pixel_change = current_target_size - size(current_ix, flexes);
let (current_target_flex, next_target_flex) =
flex_changes(current_pixel_change, current_ix, 1, flexes);
flexes[current_ix] = current_target_flex;
flexes[current_ix + 1] = next_target_flex;
};
if ix + 1 == flexes.len() {
apply_changes(ix - 1, -1.0 * amount, flexes.as_mut_slice());
} else {
apply_changes(ix, amount, flexes.as_mut_slice());
}
Some(true)
}
fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
for member in self.members.iter_mut() {
match member {
@ -625,6 +750,14 @@ impl SplitDirection {
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum ResizeIntent {
Lengthen,
Shorten,
Widen,
Narrow,
}
mod element {
use std::mem;

View file

@ -2988,6 +2988,12 @@ impl Workspace {
}
}
pub fn resize_pane(&mut self, axis: gpui::Axis, amount: Pixels, cx: &mut ViewContext<Self>) {
self.center
.resize(&self.active_pane.clone(), axis, amount, &self.bounds);
cx.notify();
}
fn handle_pane_focused(&mut self, pane: View<Pane>, cx: &mut ViewContext<Self>) {
// This is explicitly hoisted out of the following check for pane identity as
// terminal panel panes are not registered as a center panes.