Switched to vertical-first plane splitting

This commit is contained in:
Joshua Barretto 2025-11-19 22:57:10 +00:00
parent 1b4268b078
commit cffa6fe487
2 changed files with 56 additions and 50 deletions

View file

@ -21,8 +21,14 @@
## Todo
- [ ] Replace
- [ ] Terminal windows
- [ ] Terminal buffers
- [ ] Ability to resize panes (weighted sum?)
- [ ] Matching delimiter highlighting
## Issues to fix
- Undo history changes should not join so easily
- Undo history changes should not join so easily
- Double click should select ident, not highlighted token
- Switcher search should allow searching whole path, not just parent
- Scroll drag should work in opener preview
- Switching buffers should preserve scroll position

View file

@ -11,13 +11,13 @@ pub struct Pane {
last_area: Area,
}
pub struct VBox {
pub struct HBox {
selected: usize,
panes: Vec<Pane>,
last_area: Area,
}
impl Element<()> for VBox {
impl Element<()> for HBox {
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<()>, Event> {
match event.to_action(|e| {
e.to_pane_move()
@ -25,11 +25,11 @@ impl Element<()> for VBox {
.or_else(|| e.to_pane_open().map(Action::PaneOpen))
.or_else(|| e.to_pane_close())
}) {
Some(Action::PaneMove(Dir::Up)) => {
Some(Action::PaneMove(Dir::Left)) => {
self.selected = (self.selected + self.panes.len() - 1) % self.panes.len();
Ok(Resp::handled(None))
}
Some(Action::PaneMove(Dir::Down)) => {
Some(Action::PaneMove(Dir::Right)) => {
self.selected = (self.selected + 1) % self.panes.len();
Ok(Resp::handled(None))
}
@ -51,9 +51,9 @@ impl Element<()> for VBox {
}
Some(Action::PaneOpen(dir)) => {
let new_idx = match dir {
Dir::Up => self.selected.clamp(0, self.panes.len()),
Dir::Down => (self.selected + 1).min(self.panes.len()),
Dir::Left | Dir::Right => return Err(event),
Dir::Left => self.selected.clamp(0, self.panes.len()),
Dir::Right => (self.selected + 1).min(self.panes.len()),
Dir::Up | Dir::Down => return Err(event),
};
let kind = match state.buffers.keys().next() {
Some(b) => PaneKind::Doc(Doc::new(state, b)),
@ -105,20 +105,20 @@ impl Element<()> for VBox {
}
}
impl Visual for VBox {
impl Visual for HBox {
fn render(&mut self, state: &State, frame: &mut Rect) {
let n = self.panes.len();
let frame_h = frame.size()[1];
let boundary = |i| frame_h * i / n;
let frame_w = frame.size()[0];
let boundary = |i| frame_w * i / n;
self.last_area = frame.area();
for (i, pane) in self.panes.iter_mut().enumerate() {
let (y0, y1) = (boundary(i), boundary(i + 1));
let (x0, x1) = (boundary(i), boundary(i + 1));
// Draw pane contents
frame
.rect([0, y0], [frame.size()[0], y1 - y0])
.rect([x0, 0], [x1 - x0, frame.size()[0]])
.with_focus(self.selected == i)
.with(|frame| {
pane.last_area = frame.area();
@ -133,7 +133,7 @@ impl Visual for VBox {
pub struct Panes {
selected: usize,
vboxes: Vec<VBox>,
hboxes: Vec<HBox>,
last_area: Area,
}
@ -141,9 +141,9 @@ impl Panes {
pub fn new(state: &mut State, buffers: &[BufferId]) -> Self {
Self {
selected: 0,
vboxes: buffers
hboxes: buffers
.iter()
.map(|b| VBox {
.map(|b| HBox {
selected: 0,
panes: vec![Pane {
kind: PaneKind::Doc(Doc::new(state, *b)),
@ -157,12 +157,12 @@ impl Panes {
}
pub fn selected_mut(&mut self) -> Option<&mut Pane> {
let vbox = self.vboxes.get_mut(self.selected)?;
vbox.panes.get_mut(vbox.selected)
let hbox = self.hboxes.get_mut(self.selected)?;
hbox.panes.get_mut(hbox.selected)
}
pub fn selected_vbox_mut(&mut self) -> Option<&mut VBox> {
self.vboxes.get_mut(self.selected)
pub fn selected_hbox_mut(&mut self) -> Option<&mut HBox> {
self.hboxes.get_mut(self.selected)
}
}
@ -174,27 +174,27 @@ impl Element for Panes {
.or_else(|| e.to_pane_open().map(Action::PaneOpen))
.or_else(|| e.to_pane_close())
}) {
Some(Action::PaneMove(Dir::Left)) => {
self.selected = (self.selected + self.vboxes.len() - 1) % self.vboxes.len();
Some(Action::PaneMove(Dir::Up)) => {
self.selected = (self.selected + self.hboxes.len() - 1) % self.hboxes.len();
Ok(Resp::handled(None))
}
Some(Action::PaneMove(Dir::Right)) => {
self.selected = (self.selected + 1) % self.vboxes.len();
Some(Action::PaneMove(Dir::Down)) => {
self.selected = (self.selected + 1) % self.hboxes.len();
Ok(Resp::handled(None))
}
Some(Action::PaneOpen(dir @ (Dir::Left | Dir::Right))) => {
Some(Action::PaneOpen(dir @ (Dir::Up | Dir::Down))) => {
let new_idx = match dir {
Dir::Left => self.selected.clamp(0, self.vboxes.len()),
Dir::Right => (self.selected + 1).min(self.vboxes.len()),
Dir::Up => self.selected.clamp(0, self.hboxes.len()),
Dir::Down => (self.selected + 1).min(self.hboxes.len()),
_ => unreachable!(),
};
let kind = match state.buffers.keys().next() {
Some(b) => PaneKind::Doc(Doc::new(state, b)),
None => PaneKind::Empty,
};
self.vboxes.insert(
self.hboxes.insert(
new_idx,
VBox {
HBox {
selected: 0,
panes: vec![Pane {
kind,
@ -207,15 +207,15 @@ impl Element for Panes {
Ok(Resp::handled(None))
}
Some(action @ Action::Mouse(m_action, pos, is_ctrl, drag_id)) => {
for (i, vbox) in self.vboxes.iter_mut().enumerate() {
if vbox.last_area.contains(pos).is_some() {
for (i, hbox) in self.hboxes.iter_mut().enumerate() {
if hbox.last_area.contains(pos).is_some() {
if matches!(m_action, MouseAction::Click) {
self.selected = i;
}
let resp = vbox.handle(state, action.clone().into())?;
let resp = hbox.handle(state, action.clone().into())?;
if resp.is_end() {
self.vboxes.remove(self.selected);
self.selected = self.selected.min(self.vboxes.len()).saturating_sub(1);
self.hboxes.remove(self.selected);
self.selected = self.selected.min(self.hboxes.len()).saturating_sub(1);
}
return Ok(Resp::handled(resp.event));
}
@ -225,10 +225,10 @@ impl Element for Panes {
// Pass anything else through to the active pane
action => {
let mut to_handle = self.selected;
// Set selected vbox on mouse click
// Set selected hbox on mouse click
if let Some(Action::Mouse(ref m_action, pos, is_ctrl, drag_id)) = action {
for (i, vbox) in self.vboxes.iter_mut().enumerate() {
if vbox.last_area.contains(pos).is_some() {
for (i, hbox) in self.hboxes.iter_mut().enumerate() {
if hbox.last_area.contains(pos).is_some() {
if matches!(m_action, MouseAction::Click) {
self.selected = i;
}
@ -238,12 +238,12 @@ impl Element for Panes {
}
}
if let Some(vbox) = self.vboxes.get_mut(to_handle) {
// Pass to vbox
let resp = vbox.handle(state, event)?;
if let Some(hbox) = self.hboxes.get_mut(to_handle) {
// Pass to hbox
let resp = hbox.handle(state, event)?;
if resp.is_end() {
self.vboxes.remove(self.selected);
self.selected = self.selected.min(self.vboxes.len().saturating_sub(1));
self.hboxes.remove(self.selected);
self.selected = self.selected.min(self.hboxes.len().saturating_sub(1));
}
Ok(Resp::handled(resp.event))
} else {
@ -257,20 +257,20 @@ impl Element for Panes {
impl Visual for Panes {
fn render(&mut self, state: &State, frame: &mut Rect) {
let n = self.vboxes.len();
let frame_w = frame.size()[0];
let boundary = |i| frame_w * i / n;
let n = self.hboxes.len();
let frame_h = frame.size()[1];
let boundary = |i| frame_h * i / n;
self.last_area = frame.area();
for (i, vbox) in self.vboxes.iter_mut().enumerate() {
let (x0, x1) = (boundary(i), boundary(i + 1));
for (i, hbox) in self.hboxes.iter_mut().enumerate() {
let (y0, y1) = (boundary(i), boundary(i + 1));
// Draw pane contents
frame
.rect([x0, 0], [x1 - x0, frame.size()[1]])
.rect([0, y0], [frame.size()[0], y1 - y0])
.with_focus(self.selected == i)
.with(|frame| vbox.render(state, frame));
.with(|frame| hbox.render(state, frame));
}
}
}