Added basic mouse inputs
This commit is contained in:
parent
85c88402ca
commit
5925e37fba
6 changed files with 183 additions and 72 deletions
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::{state::BufferId, terminal::TerminalEvent};
|
use crate::{
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
state::BufferId,
|
||||||
|
terminal::{Area, TerminalEvent},
|
||||||
|
};
|
||||||
|
use crossterm::event::{
|
||||||
|
KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind,
|
||||||
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -14,7 +19,7 @@ pub enum Dir {
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Char(char), // Insert a character
|
Char(char), // Insert a character
|
||||||
Indent(bool), // Indent (indent vs deindent)
|
Indent(bool), // Indent (indent vs deindent)
|
||||||
Move(Dir, Dist, bool, bool), // Move the cursor (dir, page, retain_base, word)
|
Move(Dir, Dist, bool, bool), // Move the cursor (dir, dist, retain_base, word)
|
||||||
PaneMove(Dir), // Move panes
|
PaneMove(Dir), // Move panes
|
||||||
PaneOpen(Dir), // Create a new pane
|
PaneOpen(Dir), // Create a new pane
|
||||||
PaneClose, // Close the current pane
|
PaneClose, // Close the current pane
|
||||||
|
|
@ -36,6 +41,7 @@ pub enum Action {
|
||||||
SelectToken, // Fully select the token under the cursor
|
SelectToken, // Fully select the token under the cursor
|
||||||
SelectAll, // Fully select the entire input
|
SelectAll, // Fully select the entire input
|
||||||
Save, // Save the current buffer
|
Save, // Save the current buffer
|
||||||
|
Mouse(MouseAction, [isize; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How far should movement go?
|
/// How far should movement go?
|
||||||
|
|
@ -46,6 +52,13 @@ pub enum Dist {
|
||||||
Doc,
|
Doc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum MouseAction {
|
||||||
|
Click,
|
||||||
|
ScrollDown,
|
||||||
|
ScrollUp,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
// The incoming event is an action generated by some other internal component.
|
// The incoming event is an action generated by some other internal component.
|
||||||
|
|
@ -157,28 +170,33 @@ impl RawEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_move(&self) -> Option<Action> {
|
pub fn to_move(&self) -> Option<Action> {
|
||||||
let TerminalEvent::Key(KeyEvent {
|
let (dir, dist, retain_base, word) = match &self.0 {
|
||||||
code,
|
TerminalEvent::Mouse(ev) => match ev.kind {
|
||||||
modifiers,
|
MouseEventKind::ScrollUp => (Dir::Up, Dist::Char, false, false),
|
||||||
kind: KeyEventKind::Press | KeyEventKind::Repeat,
|
MouseEventKind::ScrollDown => (Dir::Down, Dist::Char, false, false),
|
||||||
..
|
_ => return None,
|
||||||
}) = &self.0
|
},
|
||||||
else {
|
TerminalEvent::Key(KeyEvent {
|
||||||
return None;
|
code,
|
||||||
};
|
modifiers,
|
||||||
|
kind: KeyEventKind::Press | KeyEventKind::Repeat,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let retain_base = modifiers.contains(KeyModifiers::SHIFT);
|
||||||
|
let word = modifiers.contains(KeyModifiers::CONTROL);
|
||||||
|
|
||||||
let retain_base = modifiers.contains(KeyModifiers::SHIFT);
|
match code {
|
||||||
let word = modifiers.contains(KeyModifiers::CONTROL);
|
KeyCode::Home => (Dir::Up, Dist::Doc, retain_base, word),
|
||||||
|
KeyCode::End => (Dir::Down, Dist::Doc, retain_base, word),
|
||||||
let (dir, dist) = match code {
|
KeyCode::PageUp => (Dir::Up, Dist::Page, retain_base, word),
|
||||||
KeyCode::Home => (Dir::Up, Dist::Doc),
|
KeyCode::PageDown => (Dir::Down, Dist::Page, retain_base, word),
|
||||||
KeyCode::End => (Dir::Down, Dist::Doc),
|
KeyCode::Left => (Dir::Left, Dist::Char, retain_base, word),
|
||||||
KeyCode::PageUp => (Dir::Up, Dist::Page),
|
KeyCode::Right => (Dir::Right, Dist::Char, retain_base, word),
|
||||||
KeyCode::PageDown => (Dir::Down, Dist::Page),
|
KeyCode::Up => (Dir::Up, Dist::Char, retain_base, word),
|
||||||
KeyCode::Left => (Dir::Left, Dist::Char),
|
KeyCode::Down => (Dir::Down, Dist::Char, retain_base, word),
|
||||||
KeyCode::Right => (Dir::Right, Dist::Char),
|
_ => return None,
|
||||||
KeyCode::Up => (Dir::Up, Dist::Char),
|
}
|
||||||
KeyCode::Down => (Dir::Down, Dist::Char),
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -390,4 +408,23 @@ impl RawEvent {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_mouse(&self, area: Area) -> Option<Action> {
|
||||||
|
let TerminalEvent::Mouse(ev) = self.0 else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(pos) = area.contains([ev.column as isize, ev.row as isize]) {
|
||||||
|
match ev.kind {
|
||||||
|
MouseEventKind::ScrollUp => Some(Action::Mouse(MouseAction::ScrollUp, pos)),
|
||||||
|
MouseEventKind::ScrollDown => Some(Action::Mouse(MouseAction::ScrollDown, pos)),
|
||||||
|
MouseEventKind::Down(MouseButton::Left) => {
|
||||||
|
Some(Action::Mouse(MouseAction::Click, pos))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ mod theme;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, Dir, Dist, Event},
|
action::{Action, Dir, Dist, Event, MouseAction},
|
||||||
state::State,
|
state::State,
|
||||||
terminal::{Color, Terminal, TerminalEvent},
|
terminal::{Area, Color, Terminal, TerminalEvent},
|
||||||
ui::{Element as _, Visual as _},
|
ui::{Element as _, Visual as _},
|
||||||
};
|
};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
|
||||||
|
|
@ -216,11 +216,11 @@ impl Buffer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn goto_line_cursor(&mut self, cursor_id: CursorId, line: isize) {
|
pub fn goto_cursor(&mut self, cursor_id: CursorId, pos: [isize; 2]) {
|
||||||
let Some(cursor) = self.cursors.get_mut(cursor_id) else {
|
let Some(cursor) = self.cursors.get_mut(cursor_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
cursor.pos = self.text.to_pos([0, line]);
|
cursor.pos = self.text.to_pos(pos);
|
||||||
cursor.reset_desired_col(&self.text);
|
cursor.reset_desired_col(&self.text);
|
||||||
cursor.base = cursor.pos;
|
cursor.base = cursor.pos;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,38 @@ impl Default for Cell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents an area of the terminal window
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
pub struct Area {
|
||||||
|
origin: [u16; 2],
|
||||||
|
size: [u16; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Area {
|
||||||
|
pub fn size(&self) -> [usize; 2] {
|
||||||
|
self.size.map(|e| e as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, pos: [isize; 2]) -> Option<[isize; 2]> {
|
||||||
|
if (self.origin[0] as isize..self.origin[0] as isize + self.size[0] as isize)
|
||||||
|
.contains(&pos[0])
|
||||||
|
&& (self.origin[1] as isize..self.origin[1] as isize + self.size[1] as isize)
|
||||||
|
.contains(&pos[1])
|
||||||
|
{
|
||||||
|
Some([
|
||||||
|
pos[0] - self.origin[0] as isize,
|
||||||
|
pos[1] - self.origin[1] as isize,
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Rect<'a> {
|
pub struct Rect<'a> {
|
||||||
fg: Color,
|
fg: Color,
|
||||||
bg: Color,
|
bg: Color,
|
||||||
origin: [u16; 2],
|
area: Area,
|
||||||
size: [u16; 2],
|
|
||||||
fb: &'a mut Framebuffer,
|
fb: &'a mut Framebuffer,
|
||||||
has_focus: bool,
|
has_focus: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -44,8 +71,8 @@ impl<'a> Rect<'a> {
|
||||||
fn get_mut(&mut self, pos: [usize; 2]) -> Option<&mut Cell> {
|
fn get_mut(&mut self, pos: [usize; 2]) -> Option<&mut Cell> {
|
||||||
if pos[0] < self.size()[0] && pos[1] < self.size()[1] {
|
if pos[0] < self.size()[0] && pos[1] < self.size()[1] {
|
||||||
let offs = [
|
let offs = [
|
||||||
self.origin[0] as usize + pos[0],
|
self.area.origin[0] as usize + pos[0],
|
||||||
self.origin[1] as usize + pos[1],
|
self.area.origin[1] as usize + pos[1],
|
||||||
];
|
];
|
||||||
Some(&mut self.fb.cells[offs[1] * self.fb.size[0] as usize + offs[0]])
|
Some(&mut self.fb.cells[offs[1] * self.fb.size[0] as usize + offs[0]])
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -59,14 +86,16 @@ impl<'a> Rect<'a> {
|
||||||
|
|
||||||
pub fn rect(&mut self, origin: [usize; 2], size: [usize; 2]) -> Rect {
|
pub fn rect(&mut self, origin: [usize; 2], size: [usize; 2]) -> Rect {
|
||||||
Rect {
|
Rect {
|
||||||
origin: [
|
area: Area {
|
||||||
self.origin[0] + origin[0] as u16,
|
origin: [
|
||||||
self.origin[1] + origin[1] as u16,
|
self.area.origin[0] + origin[0] as u16,
|
||||||
],
|
self.area.origin[1] + origin[1] as u16,
|
||||||
size: [
|
],
|
||||||
size[0].min((self.size[0] as usize).saturating_sub(origin[0])) as u16,
|
size: [
|
||||||
size[1].min((self.size[1] as usize).saturating_sub(origin[1])) as u16,
|
size[0].min((self.area.size[0] as usize).saturating_sub(origin[0])) as u16,
|
||||||
],
|
size[1].min((self.area.size[1] as usize).saturating_sub(origin[1])) as u16,
|
||||||
|
],
|
||||||
|
},
|
||||||
fg: self.fg,
|
fg: self.fg,
|
||||||
bg: self.bg,
|
bg: self.bg,
|
||||||
fb: self.fb,
|
fb: self.fb,
|
||||||
|
|
@ -132,8 +161,7 @@ impl<'a> Rect<'a> {
|
||||||
Rect {
|
Rect {
|
||||||
fg,
|
fg,
|
||||||
bg: self.bg,
|
bg: self.bg,
|
||||||
origin: self.origin,
|
area: self.area,
|
||||||
size: self.size,
|
|
||||||
fb: self.fb,
|
fb: self.fb,
|
||||||
has_focus: self.has_focus,
|
has_focus: self.has_focus,
|
||||||
}
|
}
|
||||||
|
|
@ -143,8 +171,7 @@ impl<'a> Rect<'a> {
|
||||||
Rect {
|
Rect {
|
||||||
fg: self.fg,
|
fg: self.fg,
|
||||||
bg,
|
bg,
|
||||||
origin: self.origin,
|
area: self.area,
|
||||||
size: self.size,
|
|
||||||
fb: self.fb,
|
fb: self.fb,
|
||||||
has_focus: self.has_focus,
|
has_focus: self.has_focus,
|
||||||
}
|
}
|
||||||
|
|
@ -154,8 +181,7 @@ impl<'a> Rect<'a> {
|
||||||
Rect {
|
Rect {
|
||||||
fg: self.fg,
|
fg: self.fg,
|
||||||
bg: self.bg,
|
bg: self.bg,
|
||||||
origin: self.origin,
|
area: self.area,
|
||||||
size: self.size,
|
|
||||||
fb: self.fb,
|
fb: self.fb,
|
||||||
has_focus: self.has_focus && focus,
|
has_focus: self.has_focus && focus,
|
||||||
}
|
}
|
||||||
|
|
@ -165,8 +191,12 @@ impl<'a> Rect<'a> {
|
||||||
self.has_focus
|
self.has_focus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn area(&self) -> Area {
|
||||||
|
self.area
|
||||||
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> [usize; 2] {
|
pub fn size(&self) -> [usize; 2] {
|
||||||
self.size.map(|e| e as usize)
|
self.area.size.map(|e| e as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill(&mut self, c: char) -> Rect {
|
pub fn fill(&mut self, c: char) -> Rect {
|
||||||
|
|
@ -211,8 +241,8 @@ impl<'a> Rect<'a> {
|
||||||
{
|
{
|
||||||
self.fb.cursor = Some((
|
self.fb.cursor = Some((
|
||||||
[
|
[
|
||||||
self.origin[0] + cursor[0] as u16,
|
self.area.origin[0] + cursor[0] as u16,
|
||||||
self.origin[1] + cursor[1] as u16,
|
self.area.origin[1] + cursor[1] as u16,
|
||||||
],
|
],
|
||||||
style,
|
style,
|
||||||
));
|
));
|
||||||
|
|
@ -233,8 +263,10 @@ impl Framebuffer {
|
||||||
Rect {
|
Rect {
|
||||||
fg: Color::Reset,
|
fg: Color::Reset,
|
||||||
bg: Color::Reset,
|
bg: Color::Reset,
|
||||||
origin: [0, 0],
|
area: Area {
|
||||||
size: self.size,
|
origin: [0, 0],
|
||||||
|
size: self.size,
|
||||||
|
},
|
||||||
fb: self,
|
fb: self,
|
||||||
has_focus: true,
|
has_focus: true,
|
||||||
}
|
}
|
||||||
|
|
@ -251,12 +283,14 @@ impl<'a> Terminal<'a> {
|
||||||
fn enter(mut stdout: impl io::Write) {
|
fn enter(mut stdout: impl io::Write) {
|
||||||
let _ = terminal::enable_raw_mode();
|
let _ = terminal::enable_raw_mode();
|
||||||
let _ = stdout.execute(terminal::EnterAlternateScreen);
|
let _ = stdout.execute(terminal::EnterAlternateScreen);
|
||||||
|
let _ = stdout.execute(event::EnableMouseCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn leave(mut stdout: impl io::Write) {
|
fn leave(mut stdout: impl io::Write) {
|
||||||
let _ = terminal::disable_raw_mode();
|
let _ = terminal::disable_raw_mode();
|
||||||
let _ = stdout.execute(terminal::LeaveAlternateScreen);
|
let _ = stdout.execute(terminal::LeaveAlternateScreen);
|
||||||
let _ = stdout.execute(cursor::Show);
|
let _ = stdout.execute(cursor::Show);
|
||||||
|
let _ = stdout.execute(event::DisableMouseCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with<T>(
|
pub fn with<T>(
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ pub struct Input {
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
// x/y location in the buffer that the pane is trying to focus on
|
// x/y location in the buffer that the pane is trying to focus on
|
||||||
pub focus: [isize; 2],
|
pub focus: [isize; 2],
|
||||||
// Remember the last known size for things like scrolling
|
// Remember the last area for things like scrolling
|
||||||
pub last_size: [usize; 2],
|
pub last_area: Area,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
|
|
@ -39,7 +39,7 @@ impl Input {
|
||||||
pub fn focus(&mut self, coord: [isize; 2]) {
|
pub fn focus(&mut self, coord: [isize; 2]) {
|
||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
self.focus[i] =
|
self.focus[i] =
|
||||||
self.focus[i].clamp(coord[i] - self.last_size[i] as isize + 1, coord[i]);
|
self.focus[i].clamp(coord[i] - self.last_area.size()[i] as isize + 1, coord[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,6 +64,7 @@ impl Input {
|
||||||
.or_else(|| e.to_select_token())
|
.or_else(|| e.to_select_token())
|
||||||
.or_else(|| e.to_select_all())
|
.or_else(|| e.to_select_all())
|
||||||
.or_else(|| e.to_indent())
|
.or_else(|| e.to_indent())
|
||||||
|
.or_else(|| e.to_mouse(self.last_area))
|
||||||
}) {
|
}) {
|
||||||
Some(Action::Char(c)) => {
|
Some(Action::Char(c)) => {
|
||||||
if c == '\x08' {
|
if c == '\x08' {
|
||||||
|
|
@ -81,7 +82,7 @@ impl Input {
|
||||||
Some(Action::Move(dir, dist, retain_base, word)) => {
|
Some(Action::Move(dir, dist, retain_base, word)) => {
|
||||||
let dist = match dist {
|
let dist = match dist {
|
||||||
Dist::Char => [1, 1],
|
Dist::Char => [1, 1],
|
||||||
Dist::Page => self.last_size.map(|s| s.saturating_sub(3).max(1)),
|
Dist::Page => self.last_area.size().map(|s| s.saturating_sub(3).max(1)),
|
||||||
// TODO: Don't just use an arbitrary very large number
|
// TODO: Don't just use an arbitrary very large number
|
||||||
Dist::Doc => [1_000_000_000; 2],
|
Dist::Doc => [1_000_000_000; 2],
|
||||||
};
|
};
|
||||||
|
|
@ -94,7 +95,7 @@ impl Input {
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(Action::GotoLine(line)) => {
|
Some(Action::GotoLine(line)) => {
|
||||||
buffer.goto_line_cursor(cursor_id, line);
|
buffer.goto_cursor(cursor_id, [0, line]);
|
||||||
self.refocus(buffer, cursor_id);
|
self.refocus(buffer, cursor_id);
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +107,11 @@ impl Input {
|
||||||
buffer.select_all_cursor(cursor_id);
|
buffer.select_all_cursor(cursor_id);
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
|
Some(Action::Mouse(MouseAction::Click, pos)) => {
|
||||||
|
buffer.goto_cursor(cursor_id, [self.focus[0] + pos[0], self.focus[1] + pos[1]]);
|
||||||
|
self.refocus(buffer, cursor_id);
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
_ => Err(event),
|
_ => Err(event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +147,7 @@ impl Input {
|
||||||
Mode::Doc => line_num_w + 2,
|
Mode::Doc => line_num_w + 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.last_size = [frame.size()[0].saturating_sub(margin_w), frame.size()[1]];
|
self.last_area = frame.rect([margin_w, 0], [!0, !0]).area();
|
||||||
|
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
for (i, (line_num, (line_pos, line))) in buffer
|
for (i, (line_num, (line_pos, line))) in buffer
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::state::BufferId;
|
use crate::state::BufferId;
|
||||||
|
|
||||||
pub enum Pane {
|
pub enum PaneKind {
|
||||||
Empty,
|
Empty,
|
||||||
Doc(Doc),
|
Doc(Doc),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Pane {
|
||||||
|
kind: PaneKind,
|
||||||
|
last_area: Area,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Panes {
|
pub struct Panes {
|
||||||
selected: usize,
|
selected: usize,
|
||||||
panes: Vec<Pane>,
|
panes: Vec<Pane>,
|
||||||
|
last_area: Area,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Panes {
|
impl Panes {
|
||||||
|
|
@ -17,8 +23,12 @@ impl Panes {
|
||||||
selected: 0,
|
selected: 0,
|
||||||
panes: buffers
|
panes: buffers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|b| Pane::Doc(Doc::new(state, *b)))
|
.map(|b| Pane {
|
||||||
|
kind: PaneKind::Doc(Doc::new(state, *b)),
|
||||||
|
last_area: Area::default(),
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
last_area: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,6 +44,7 @@ impl Element for Panes {
|
||||||
.map(Action::PaneMove)
|
.map(Action::PaneMove)
|
||||||
.or_else(|| e.to_pane_open().map(Action::PaneOpen))
|
.or_else(|| e.to_pane_open().map(Action::PaneOpen))
|
||||||
.or_else(|| e.to_pane_close())
|
.or_else(|| e.to_pane_close())
|
||||||
|
.or_else(|| e.to_mouse(self.last_area))
|
||||||
}) {
|
}) {
|
||||||
Some(Action::PaneMove(Dir::Left)) => {
|
Some(Action::PaneMove(Dir::Left)) => {
|
||||||
self.selected = (self.selected + self.panes.len() - 1) % self.panes.len();
|
self.selected = (self.selected + self.panes.len() - 1) % self.panes.len();
|
||||||
|
|
@ -45,9 +56,9 @@ impl Element for Panes {
|
||||||
}
|
}
|
||||||
Some(Action::PaneClose) => {
|
Some(Action::PaneClose) => {
|
||||||
if self.selected < self.panes.len() {
|
if self.selected < self.panes.len() {
|
||||||
match self.panes.remove(self.selected) {
|
match self.panes.remove(self.selected).kind {
|
||||||
Pane::Empty => {}
|
PaneKind::Empty => {}
|
||||||
Pane::Doc(doc) => doc.close(state),
|
PaneKind::Doc(doc) => doc.close(state),
|
||||||
}
|
}
|
||||||
self.selected = self.selected.clamp(0, self.panes.len().saturating_sub(1));
|
self.selected = self.selected.clamp(0, self.panes.len().saturating_sub(1));
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
|
|
@ -61,21 +72,39 @@ impl Element for Panes {
|
||||||
Dir::Right => (self.selected + 1).min(self.panes.len()),
|
Dir::Right => (self.selected + 1).min(self.panes.len()),
|
||||||
Dir::Up | Dir::Down => return Err(event),
|
Dir::Up | Dir::Down => return Err(event),
|
||||||
};
|
};
|
||||||
let pane = match state.buffers.keys().next() {
|
let kind = match state.buffers.keys().next() {
|
||||||
Some(b) => Pane::Doc(Doc::new(state, b)),
|
Some(b) => PaneKind::Doc(Doc::new(state, b)),
|
||||||
None => Pane::Empty,
|
None => PaneKind::Empty,
|
||||||
};
|
};
|
||||||
self.panes.insert(new_idx, pane);
|
self.panes.insert(
|
||||||
|
new_idx,
|
||||||
|
Pane {
|
||||||
|
kind,
|
||||||
|
last_area: Area::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
self.selected = new_idx;
|
self.selected = new_idx;
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
|
Some(Action::Mouse(_, pos)) => {
|
||||||
|
for (i, pane) in self.panes.iter_mut().enumerate() {
|
||||||
|
if pane.last_area.contains(pos).is_some() {
|
||||||
|
self.selected = i;
|
||||||
|
match &mut pane.kind {
|
||||||
|
PaneKind::Doc(doc) => return doc.handle(state, event),
|
||||||
|
PaneKind::Empty => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
// Pass anything else through to the active pane
|
// Pass anything else through to the active pane
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(pane) = self.panes.get_mut(self.selected) {
|
if let Some(pane) = self.panes.get_mut(self.selected) {
|
||||||
// Pass to pane
|
// Pass to pane
|
||||||
match pane {
|
match &mut pane.kind {
|
||||||
Pane::Empty => Err(event),
|
PaneKind::Empty => Err(event),
|
||||||
Pane::Doc(doc) => doc.handle(state, event),
|
PaneKind::Doc(doc) => doc.handle(state, event),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No active pane, don't handle
|
// No active pane, don't handle
|
||||||
|
|
@ -92,6 +121,8 @@ impl Visual for Panes {
|
||||||
let frame_w = frame.size()[0];
|
let frame_w = frame.size()[0];
|
||||||
let boundary = |i| frame_w * i / n;
|
let boundary = |i| frame_w * i / n;
|
||||||
|
|
||||||
|
self.last_area = frame.area();
|
||||||
|
|
||||||
for (i, pane) in self.panes.iter_mut().enumerate() {
|
for (i, pane) in self.panes.iter_mut().enumerate() {
|
||||||
let (x0, x1) = (boundary(i), boundary(i + 1));
|
let (x0, x1) = (boundary(i), boundary(i + 1));
|
||||||
|
|
||||||
|
|
@ -99,9 +130,12 @@ impl Visual for Panes {
|
||||||
frame
|
frame
|
||||||
.rect([x0, 0], [x1 - x0, frame.size()[1]])
|
.rect([x0, 0], [x1 - x0, frame.size()[1]])
|
||||||
.with_focus(self.selected == i)
|
.with_focus(self.selected == i)
|
||||||
.with(|frame| match pane {
|
.with(|frame| {
|
||||||
Pane::Empty => {}
|
pane.last_area = frame.area();
|
||||||
Pane::Doc(doc) => doc.render(state, frame),
|
match &mut pane.kind {
|
||||||
|
PaneKind::Empty => {}
|
||||||
|
PaneKind::Doc(doc) => doc.render(state, frame),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue