Added pane open/close

This commit is contained in:
Joshua Barretto 2025-06-08 20:37:05 +01:00
parent 81ab27cbbf
commit 84056b5a74
3 changed files with 100 additions and 2 deletions

23
README.md Normal file
View file

@ -0,0 +1,23 @@
# ZTE
## Features
- [ ] Buffers
- [ ] Buffer switching
- [ ] Prompt
- [ ] Cursor selection
- [ ] Basic cursor movement
- [ ] Multiple panes
- [ ] Pane creation/deletion
## Todo
- [ ] Opener
- [ ] Find
- [ ] Replace
- [ ] Project search
- [ ] Search in buffer switcher
- [ ] File saving
- [ ] Syntax highlighting
- [ ] Auto-indent (and related features)
- [ ] Undo/redo

View file

@ -15,6 +15,8 @@ pub enum Action {
Backspace, // Backspace a character
Move(Dir, bool, bool), // Move the cursor (dir, page, retain_base)
PaneMove(Dir), // Move panes
PaneOpen(Dir), // Create a new pane
PaneClose, // Close the current pane
Cancel, // Cancels the current context
Go, // Search, accept, or select the current option
Yes, // A binary confirmation is answered 'yes'
@ -26,6 +28,7 @@ pub enum Action {
SwitchBuffer(BufferId), // Switch the current pane to the given buffer
}
#[derive(Debug)]
pub enum Event {
// The incoming event is an action generated by some other internal component.
Action(Action),
@ -49,6 +52,9 @@ impl Event {
}
}
const ALT_SHIFT: KeyModifiers = KeyModifiers::ALT.union(KeyModifiers::SHIFT);
#[derive(Debug)]
pub struct RawEvent(TerminalEvent);
impl RawEvent {
@ -92,6 +98,40 @@ impl RawEvent {
}
}
pub fn to_pane_open(&self) -> Option<Dir> {
match &self.0 {
TerminalEvent::Key(KeyEvent {
code,
modifiers: ALT_SHIFT,
kind: KeyEventKind::Press | KeyEventKind::Repeat,
..
}) => match code {
KeyCode::Char('A') => Some(Dir::Left),
KeyCode::Char('D') => Some(Dir::Right),
KeyCode::Char('W') => Some(Dir::Up),
KeyCode::Char('S') => Some(Dir::Down),
_ => None,
},
_ => None,
}
}
pub fn to_pane_close(&self) -> Option<Action> {
if matches!(
&self.0,
TerminalEvent::Key(KeyEvent {
code: KeyCode::Char('q'),
modifiers: KeyModifiers::ALT,
kind: KeyEventKind::Press,
..
})
) {
Some(Action::PaneClose)
} else {
None
}
}
pub fn to_move(&self) -> Option<Action> {
let TerminalEvent::Key(KeyEvent {
code,

View file

@ -121,7 +121,7 @@ impl Visual for Doc {
let line_num_w = buffer.text.lines().count().max(1).ilog10() as usize + 1;
let margin_w = line_num_w + 2;
self.last_size = [frame.size()[0] - margin_w, frame.size()[1]];
self.last_size = [frame.size()[0].saturating_sub(margin_w), frame.size()[1]];
let mut pos = 0;
for (i, (line_num, (line_pos, line))) in buffer
@ -184,12 +184,14 @@ impl Visual for Doc {
#[derive(Clone)]
pub enum Pane {
Empty,
Doc(Doc),
}
impl Pane {
fn title(&self, state: &State) -> Option<String> {
match self {
Self::Empty => None,
Self::Doc(doc) => {
let Some(buffer) = state.buffers.get(doc.buffer) else {
return None;
@ -219,7 +221,12 @@ impl Panes {
impl Element for Panes {
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp, Event> {
match event.to_action(|e| e.to_pane_move().map(Action::PaneMove)) {
match event.to_action(|e| {
e.to_pane_move()
.map(Action::PaneMove)
.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.panes.len() - 1) % self.panes.len();
Ok(Resp::handled(None))
@ -228,11 +235,38 @@ impl Element for Panes {
self.selected = (self.selected + 1) % self.panes.len();
Ok(Resp::handled(None))
}
Some(Action::PaneClose) => {
if self.selected < self.panes.len() {
match self.panes.remove(self.selected) {
Pane::Empty => {}
Pane::Doc(doc) => doc.close(state),
}
self.selected = self.selected.clamp(0, self.panes.len().saturating_sub(1));
Ok(Resp::handled(None))
} else {
Err(event)
}
}
Some(Action::PaneOpen(dir)) => {
let new_idx = match dir {
Dir::Left => self.selected.saturating_sub(1).clamp(0, self.panes.len()),
Dir::Right => (self.selected + 1).min(self.panes.len()),
Dir::Up | Dir::Down => return Err(event),
};
let pane = match state.buffers.keys().next() {
Some(b) => Pane::Doc(Doc::new(state, b)),
None => Pane::Empty,
};
self.panes.insert(new_idx, pane);
self.selected = new_idx;
Ok(Resp::handled(None))
}
// Pass anything else through to the active pane
err => {
if let Some(pane) = self.panes.get_mut(self.selected) {
// Pass to pane
match pane {
Pane::Empty => Err(event),
Pane::Doc(doc) => doc.handle(state, event),
}
} else {
@ -266,6 +300,7 @@ impl Visual for Panes {
.with_border(border_theme, pane.title(state).as_deref())
.with_focus(is_selected)
.with(|frame| match pane {
Pane::Empty => {}
Pane::Doc(doc) => doc.render(state, frame),
});
}