Added vertical pane support
This commit is contained in:
parent
4b5579c726
commit
1b4268b078
3 changed files with 184 additions and 44 deletions
|
|
@ -7,7 +7,7 @@ use crossterm::event::{
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Dir {
|
pub enum Dir {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
|
|
@ -65,7 +65,7 @@ pub enum Dist {
|
||||||
Doc,
|
Doc,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum MouseAction {
|
pub enum MouseAction {
|
||||||
Click,
|
Click,
|
||||||
Drag,
|
Drag,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ pub struct Input {
|
||||||
// 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 area for things like scrolling
|
// Remember the last area for things like scrolling
|
||||||
|
pub frame_area: Area,
|
||||||
pub last_area: Area,
|
pub last_area: Area,
|
||||||
pub last_scroll_pos: Option<([isize; 2], usize, usize)>,
|
pub last_scroll_pos: Option<([isize; 2], usize, usize)>,
|
||||||
pub scroll_grab: Option<(usize, isize)>,
|
pub scroll_grab: Option<(usize, isize)>,
|
||||||
|
|
@ -147,6 +148,7 @@ impl Input {
|
||||||
}
|
}
|
||||||
Some(Action::Mouse(MouseAction::Click, pos, false, drag_id)) => {
|
Some(Action::Mouse(MouseAction::Click, pos, false, drag_id)) => {
|
||||||
if let Some((scroll_pos, h, _)) = self.last_scroll_pos
|
if let Some((scroll_pos, h, _)) = self.last_scroll_pos
|
||||||
|
&& let Some(pos) = self.frame_area.contains(pos)
|
||||||
&& scroll_pos[0] == pos[0]
|
&& scroll_pos[0] == pos[0]
|
||||||
&& (scroll_pos[1]..=scroll_pos[1] + h as isize).contains(&pos[1])
|
&& (scroll_pos[1]..=scroll_pos[1] + h as isize).contains(&pos[1])
|
||||||
{
|
{
|
||||||
|
|
@ -169,6 +171,7 @@ impl Input {
|
||||||
if self.scroll_grab.map_or(false, |(di, _)| di == drag_id) =>
|
if self.scroll_grab.map_or(false, |(di, _)| di == drag_id) =>
|
||||||
{
|
{
|
||||||
if let Some((_, offset)) = self.scroll_grab
|
if let Some((_, offset)) = self.scroll_grab
|
||||||
|
&& let Some(pos) = self.frame_area.contains(pos)
|
||||||
&& let Some((_, scroll_sz, frame_sz)) = self.last_scroll_pos
|
&& let Some((_, scroll_sz, frame_sz)) = self.last_scroll_pos
|
||||||
{
|
{
|
||||||
self.focus[1] = ((pos[1] - offset).max(0) as usize
|
self.focus[1] = ((pos[1] - offset).max(0) as usize
|
||||||
|
|
@ -181,11 +184,13 @@ impl Input {
|
||||||
Action::Mouse(MouseAction::Drag, pos, false, _)
|
Action::Mouse(MouseAction::Drag, pos, false, _)
|
||||||
| Action::Mouse(MouseAction::Click, pos, true, _),
|
| Action::Mouse(MouseAction::Click, pos, true, _),
|
||||||
) => {
|
) => {
|
||||||
buffer.goto_cursor(
|
if let Some(pos) = self.frame_area.contains(pos) {
|
||||||
cursor_id,
|
buffer.goto_cursor(
|
||||||
[self.focus[0] + pos[0], self.focus[1] + pos[1]],
|
cursor_id,
|
||||||
false,
|
[self.focus[0] + pos[0], self.focus[1] + pos[1]],
|
||||||
);
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(Action::Undo) => {
|
Some(Action::Undo) => {
|
||||||
|
|
@ -250,6 +255,8 @@ impl Input {
|
||||||
finder: Option<&Finder>,
|
finder: Option<&Finder>,
|
||||||
outer_frame: &mut Rect,
|
outer_frame: &mut Rect,
|
||||||
) {
|
) {
|
||||||
|
self.frame_area = outer_frame.area();
|
||||||
|
|
||||||
// Add frame
|
// Add frame
|
||||||
let mut frame = if matches!(self.mode, Mode::SearchResult) {
|
let mut frame = if matches!(self.mode, Mode::SearchResult) {
|
||||||
outer_frame.rect([0; 2], [!0; 2])
|
outer_frame.rect([0; 2], [!0; 2])
|
||||||
|
|
|
||||||
207
src/ui/panes.rs
207
src/ui/panes.rs
|
|
@ -11,45 +11,25 @@ pub struct Pane {
|
||||||
last_area: Area,
|
last_area: Area,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Panes {
|
pub struct VBox {
|
||||||
selected: usize,
|
selected: usize,
|
||||||
panes: Vec<Pane>,
|
panes: Vec<Pane>,
|
||||||
last_area: Area,
|
last_area: Area,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Panes {
|
impl Element<()> for VBox {
|
||||||
pub fn new(state: &mut State, buffers: &[BufferId]) -> Self {
|
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<()>, Event> {
|
||||||
Self {
|
|
||||||
selected: 0,
|
|
||||||
panes: buffers
|
|
||||||
.iter()
|
|
||||||
.map(|b| Pane {
|
|
||||||
kind: PaneKind::Doc(Doc::new(state, *b)),
|
|
||||||
last_area: Area::default(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
last_area: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected_mut(&mut self) -> Option<&mut Pane> {
|
|
||||||
self.panes.get_mut(self.selected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element for Panes {
|
|
||||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp, Event> {
|
|
||||||
match event.to_action(|e| {
|
match event.to_action(|e| {
|
||||||
e.to_pane_move()
|
e.to_pane_move()
|
||||||
.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())
|
||||||
}) {
|
}) {
|
||||||
Some(Action::PaneMove(Dir::Left)) => {
|
Some(Action::PaneMove(Dir::Up)) => {
|
||||||
self.selected = (self.selected + self.panes.len() - 1) % self.panes.len();
|
self.selected = (self.selected + self.panes.len() - 1) % self.panes.len();
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(Action::PaneMove(Dir::Right)) => {
|
Some(Action::PaneMove(Dir::Down)) => {
|
||||||
self.selected = (self.selected + 1) % self.panes.len();
|
self.selected = (self.selected + 1) % self.panes.len();
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
|
|
@ -60,16 +40,20 @@ impl Element for Panes {
|
||||||
PaneKind::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))
|
if self.panes.is_empty() {
|
||||||
|
Ok(Resp::end(None))
|
||||||
|
} else {
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(event)
|
Err(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Action::PaneOpen(dir)) => {
|
Some(Action::PaneOpen(dir)) => {
|
||||||
let new_idx = match dir {
|
let new_idx = match dir {
|
||||||
Dir::Left => self.selected.saturating_sub(1).clamp(0, self.panes.len()),
|
Dir::Up => self.selected.clamp(0, self.panes.len()),
|
||||||
Dir::Right => (self.selected + 1).min(self.panes.len()),
|
Dir::Down => (self.selected + 1).min(self.panes.len()),
|
||||||
Dir::Up | Dir::Down => return Err(event),
|
Dir::Left | Dir::Right => return Err(event),
|
||||||
};
|
};
|
||||||
let kind = match state.buffers.keys().next() {
|
let kind = match state.buffers.keys().next() {
|
||||||
Some(b) => PaneKind::Doc(Doc::new(state, b)),
|
Some(b) => PaneKind::Doc(Doc::new(state, b)),
|
||||||
|
|
@ -85,16 +69,21 @@ impl Element for Panes {
|
||||||
self.selected = new_idx;
|
self.selected = new_idx;
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(ref action @ Action::Mouse(ref m_action, pos, _, _)) => {
|
Some(action @ Action::Mouse(m_action, pos, is_ctrl, drag_id)) => {
|
||||||
for (i, pane) in self.panes.iter_mut().enumerate() {
|
for (i, pane) in self.panes.iter_mut().enumerate() {
|
||||||
if pane.last_area.contains(pos).is_some() {
|
if pane.last_area.contains(pos).is_some() {
|
||||||
if matches!(m_action, MouseAction::Click) {
|
if matches!(m_action, MouseAction::Click) {
|
||||||
self.selected = i;
|
self.selected = i;
|
||||||
}
|
}
|
||||||
match &mut pane.kind {
|
match &mut pane.kind {
|
||||||
PaneKind::Doc(doc) => return doc.handle(state, action.clone().into()),
|
PaneKind::Doc(doc) => {
|
||||||
|
return doc
|
||||||
|
.handle(state, action.clone().into())
|
||||||
|
.map(Resp::into_can_end);
|
||||||
|
}
|
||||||
PaneKind::Empty => {}
|
PaneKind::Empty => {}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
|
|
@ -105,7 +94,7 @@ impl Element for Panes {
|
||||||
// Pass to pane
|
// Pass to pane
|
||||||
match &mut pane.kind {
|
match &mut pane.kind {
|
||||||
PaneKind::Empty => Err(event),
|
PaneKind::Empty => Err(event),
|
||||||
PaneKind::Doc(doc) => doc.handle(state, event),
|
PaneKind::Doc(doc) => doc.handle(state, event).map(Resp::into_can_end),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No active pane, don't handle
|
// No active pane, don't handle
|
||||||
|
|
@ -116,20 +105,20 @@ impl Element for Panes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visual for Panes {
|
impl Visual for VBox {
|
||||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||||
let n = self.panes.len();
|
let n = self.panes.len();
|
||||||
let frame_w = frame.size()[0];
|
let frame_h = frame.size()[1];
|
||||||
let boundary = |i| frame_w * i / n;
|
let boundary = |i| frame_h * i / n;
|
||||||
|
|
||||||
self.last_area = frame.area();
|
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 (y0, y1) = (boundary(i), boundary(i + 1));
|
||||||
|
|
||||||
// Draw pane contents
|
// Draw pane contents
|
||||||
frame
|
frame
|
||||||
.rect([x0, 0], [x1 - x0, frame.size()[1]])
|
.rect([0, y0], [frame.size()[0], y1 - y0])
|
||||||
.with_focus(self.selected == i)
|
.with_focus(self.selected == i)
|
||||||
.with(|frame| {
|
.with(|frame| {
|
||||||
pane.last_area = frame.area();
|
pane.last_area = frame.area();
|
||||||
|
|
@ -141,3 +130,147 @@ impl Visual for Panes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Panes {
|
||||||
|
selected: usize,
|
||||||
|
vboxes: Vec<VBox>,
|
||||||
|
last_area: Area,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Panes {
|
||||||
|
pub fn new(state: &mut State, buffers: &[BufferId]) -> Self {
|
||||||
|
Self {
|
||||||
|
selected: 0,
|
||||||
|
vboxes: buffers
|
||||||
|
.iter()
|
||||||
|
.map(|b| VBox {
|
||||||
|
selected: 0,
|
||||||
|
panes: vec![Pane {
|
||||||
|
kind: PaneKind::Doc(Doc::new(state, *b)),
|
||||||
|
last_area: Area::default(),
|
||||||
|
}],
|
||||||
|
last_area: Area::default(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
last_area: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_mut(&mut self) -> Option<&mut Pane> {
|
||||||
|
let vbox = self.vboxes.get_mut(self.selected)?;
|
||||||
|
vbox.panes.get_mut(vbox.selected)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_vbox_mut(&mut self) -> Option<&mut VBox> {
|
||||||
|
self.vboxes.get_mut(self.selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
.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();
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
|
Some(Action::PaneMove(Dir::Right)) => {
|
||||||
|
self.selected = (self.selected + 1) % self.vboxes.len();
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
|
Some(Action::PaneOpen(dir @ (Dir::Left | Dir::Right))) => {
|
||||||
|
let new_idx = match dir {
|
||||||
|
Dir::Left => self.selected.clamp(0, self.vboxes.len()),
|
||||||
|
Dir::Right => (self.selected + 1).min(self.vboxes.len()),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let kind = match state.buffers.keys().next() {
|
||||||
|
Some(b) => PaneKind::Doc(Doc::new(state, b)),
|
||||||
|
None => PaneKind::Empty,
|
||||||
|
};
|
||||||
|
self.vboxes.insert(
|
||||||
|
new_idx,
|
||||||
|
VBox {
|
||||||
|
selected: 0,
|
||||||
|
panes: vec![Pane {
|
||||||
|
kind,
|
||||||
|
last_area: Area::default(),
|
||||||
|
}],
|
||||||
|
last_area: Area::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.selected = new_idx;
|
||||||
|
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() {
|
||||||
|
if matches!(m_action, MouseAction::Click) {
|
||||||
|
self.selected = i;
|
||||||
|
}
|
||||||
|
let resp = vbox.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);
|
||||||
|
}
|
||||||
|
return Ok(Resp::handled(resp.event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
|
// Pass anything else through to the active pane
|
||||||
|
action => {
|
||||||
|
let mut to_handle = self.selected;
|
||||||
|
// Set selected vbox 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() {
|
||||||
|
if matches!(m_action, MouseAction::Click) {
|
||||||
|
self.selected = i;
|
||||||
|
}
|
||||||
|
to_handle = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(vbox) = self.vboxes.get_mut(to_handle) {
|
||||||
|
// Pass to vbox
|
||||||
|
let resp = vbox.handle(state, event)?;
|
||||||
|
if resp.is_end() {
|
||||||
|
self.vboxes.remove(self.selected);
|
||||||
|
self.selected = self.selected.min(self.vboxes.len().saturating_sub(1));
|
||||||
|
}
|
||||||
|
Ok(Resp::handled(resp.event))
|
||||||
|
} else {
|
||||||
|
// No active pane, don't handle
|
||||||
|
Err(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
self.last_area = frame.area();
|
||||||
|
|
||||||
|
for (i, vbox) in self.vboxes.iter_mut().enumerate() {
|
||||||
|
let (x0, x1) = (boundary(i), boundary(i + 1));
|
||||||
|
|
||||||
|
// Draw pane contents
|
||||||
|
frame
|
||||||
|
.rect([x0, 0], [x1 - x0, frame.size()[1]])
|
||||||
|
.with_focus(self.selected == i)
|
||||||
|
.with(|frame| vbox.render(state, frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue