373 lines
13 KiB
Rust
373 lines
13 KiB
Rust
use super::*;
|
|
use crate::state::BufferId;
|
|
|
|
pub enum PaneKind {
|
|
Empty,
|
|
Doc(Doc),
|
|
}
|
|
|
|
enum PaneTask {
|
|
Opener(Opener),
|
|
Switcher(Switcher),
|
|
}
|
|
|
|
pub struct Pane {
|
|
kind: PaneKind,
|
|
last_area: Area,
|
|
task: Option<PaneTask>,
|
|
}
|
|
|
|
impl Element for Pane {
|
|
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp, Event> {
|
|
match event.to_action(|_| None) {
|
|
Some(Action::OpenOpener(path)) => {
|
|
self.task = Some(PaneTask::Opener(Opener::new(path)));
|
|
Ok(Resp::handled(None))
|
|
}
|
|
Some(Action::OpenSwitcher) => {
|
|
self.task = Some(PaneTask::Switcher(Switcher::new(state.most_recent())));
|
|
Ok(Resp::handled(None))
|
|
}
|
|
_ => {
|
|
let event = if let Some(task) = &mut self.task {
|
|
let resp = match task {
|
|
PaneTask::Opener(opener) => opener.handle(state, event),
|
|
PaneTask::Switcher(switcher) => switcher.handle(state, event),
|
|
};
|
|
match resp {
|
|
Ok(resp) => {
|
|
if resp.is_end() {
|
|
self.task = None;
|
|
}
|
|
return Ok(Resp::handled(resp.event));
|
|
}
|
|
Err(event) => event,
|
|
}
|
|
} else {
|
|
event
|
|
};
|
|
|
|
match &mut self.kind {
|
|
PaneKind::Empty => Err(event),
|
|
PaneKind::Doc(doc) => doc.handle(state, event),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Visual for Pane {
|
|
fn render(&mut self, state: &State, frame: &mut Rect) {
|
|
let remaining_space = match &mut self.task {
|
|
Some(PaneTask::Opener(opener)) => {
|
|
opener.render(state, frame);
|
|
None
|
|
}
|
|
Some(PaneTask::Switcher(switcher)) => {
|
|
let switcher_h = switcher.requested_height();
|
|
switcher.render(
|
|
state,
|
|
&mut frame.rect([0, frame.size()[1] - switcher_h], [!0, !0]),
|
|
);
|
|
Some(([0, 0], [!0, frame.size()[1] - switcher_h]))
|
|
}
|
|
None => Some(([0, 0], [!0, !0])),
|
|
};
|
|
|
|
if let Some((pos, sz)) = remaining_space {
|
|
match &mut self.kind {
|
|
PaneKind::Empty => {}
|
|
PaneKind::Doc(doc) => doc.render(state, &mut frame.rect(pos, sz)),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct HBox {
|
|
selected: usize,
|
|
panes: Vec<Pane>,
|
|
last_area: Area,
|
|
size_weight: f32,
|
|
}
|
|
|
|
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()
|
|
.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))
|
|
}
|
|
Some(Action::PaneMove(Dir::Right)) => {
|
|
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).kind {
|
|
PaneKind::Empty => {}
|
|
PaneKind::Doc(doc) => doc.close(state),
|
|
}
|
|
self.selected = self.selected.clamp(0, self.panes.len().saturating_sub(1));
|
|
if self.panes.is_empty() {
|
|
Ok(Resp::end(None))
|
|
} else {
|
|
Ok(Resp::handled(None))
|
|
}
|
|
} else {
|
|
Err(event)
|
|
}
|
|
}
|
|
Some(Action::PaneOpen(dir)) => {
|
|
let new_idx = match dir {
|
|
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)),
|
|
None => PaneKind::Empty,
|
|
};
|
|
self.panes.insert(
|
|
new_idx,
|
|
Pane {
|
|
kind,
|
|
last_area: Area::default(),
|
|
task: None,
|
|
},
|
|
);
|
|
self.selected = new_idx;
|
|
Ok(Resp::handled(None))
|
|
}
|
|
Some(action @ Action::Mouse(m_action, pos, is_ctrl, drag_id)) => {
|
|
for (i, pane) in self.panes.iter_mut().enumerate() {
|
|
if pane.last_area.contains(pos).is_some() {
|
|
if matches!(m_action, MouseAction::Click) {
|
|
self.selected = i;
|
|
}
|
|
return pane
|
|
.handle(state, action.clone().into())
|
|
.map(Resp::into_can_end);
|
|
}
|
|
}
|
|
Ok(Resp::handled(None))
|
|
}
|
|
// Pass anything else through to the active pane
|
|
_ => {
|
|
if let Some(pane) = self.panes.get_mut(self.selected) {
|
|
// Pass to pane
|
|
pane.handle(state, event).map(Resp::into_can_end)
|
|
} else {
|
|
// No active pane, don't handle
|
|
Err(event)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Visual for HBox {
|
|
fn render(&mut self, state: &State, frame: &mut Rect) {
|
|
let n = self.panes.len();
|
|
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 (x0, x1) = (boundary(i), boundary(i + 1));
|
|
|
|
// Draw pane contents
|
|
frame
|
|
.rect([x0, 0], [x1 - x0, frame.size()[0]])
|
|
.with_focus(self.selected == i)
|
|
.with(|frame| {
|
|
pane.last_area = frame.area();
|
|
pane.render(state, frame);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Panes {
|
|
selected: usize,
|
|
hboxes: Vec<HBox>,
|
|
last_area: Area,
|
|
}
|
|
|
|
impl Panes {
|
|
pub fn new(state: &mut State, buffers: &[BufferId]) -> Self {
|
|
Self {
|
|
selected: 0,
|
|
hboxes: buffers
|
|
.iter()
|
|
.map(|b| HBox {
|
|
selected: 0,
|
|
panes: vec![Pane {
|
|
kind: PaneKind::Doc(Doc::new(state, *b)),
|
|
last_area: Area::default(),
|
|
task: None,
|
|
}],
|
|
last_area: Area::default(),
|
|
size_weight: 1.0,
|
|
})
|
|
.collect(),
|
|
last_area: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn selected_mut(&mut self) -> Option<&mut Pane> {
|
|
let hbox = self.hboxes.get_mut(self.selected)?;
|
|
hbox.panes.get_mut(hbox.selected)
|
|
}
|
|
|
|
pub fn selected_hbox_mut(&mut self) -> Option<&mut HBox> {
|
|
self.hboxes.get_mut(self.selected)
|
|
}
|
|
|
|
fn rescale(&mut self) {
|
|
let total_weight = self.hboxes.iter().map(|h| h.size_weight).sum::<f32>();
|
|
let sz = self.last_area.size()[1] as f32;
|
|
self.hboxes
|
|
.iter_mut()
|
|
.for_each(|h| h.size_weight = (h.size_weight / total_weight).max(3.0 / sz).min(10.0));
|
|
}
|
|
}
|
|
|
|
impl Element for Panes {
|
|
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp, Event> {
|
|
let res = 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())
|
|
.or_else(|| e.to_pane_resize())
|
|
}) {
|
|
Some(Action::PaneMove(Dir::Up)) => {
|
|
self.selected = (self.selected + self.hboxes.len() - 1) % self.hboxes.len();
|
|
Ok(Resp::handled(None))
|
|
}
|
|
Some(Action::PaneMove(Dir::Down)) => {
|
|
self.selected = (self.selected + 1) % self.hboxes.len();
|
|
Ok(Resp::handled(None))
|
|
}
|
|
Some(Action::PaneOpen(dir @ (Dir::Up | Dir::Down))) => {
|
|
let new_idx = match dir {
|
|
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,
|
|
};
|
|
let size_weight = 1.0 / self.hboxes.len().max(1) as f32;
|
|
self.hboxes.insert(
|
|
new_idx,
|
|
HBox {
|
|
selected: 0,
|
|
panes: vec![Pane {
|
|
kind,
|
|
last_area: Area::default(),
|
|
task: None,
|
|
}],
|
|
last_area: Area::default(),
|
|
size_weight,
|
|
},
|
|
);
|
|
self.selected = new_idx;
|
|
Ok(Resp::handled(None))
|
|
}
|
|
Some(Action::PaneResize(by)) => {
|
|
if let Some(hbox) = self.hboxes.get_mut(self.selected) {
|
|
hbox.size_weight *= 1.3f32.powi(by);
|
|
}
|
|
Ok(Resp::handled(None))
|
|
}
|
|
Some(action @ Action::Mouse(m_action, pos, is_ctrl, drag_id)) => {
|
|
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 = hbox.handle(state, action.clone().into())?;
|
|
if resp.is_end() {
|
|
self.hboxes.remove(self.selected);
|
|
self.selected = self.selected.min(self.hboxes.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 hbox on mouse click
|
|
if let Some(Action::Mouse(ref m_action, pos, is_ctrl, drag_id)) = action {
|
|
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;
|
|
}
|
|
to_handle = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(hbox) = self.hboxes.get_mut(to_handle) {
|
|
// Pass to hbox
|
|
let resp = hbox.handle(state, event)?;
|
|
if resp.is_end() {
|
|
self.hboxes.remove(self.selected);
|
|
self.selected = self.selected.min(self.hboxes.len().saturating_sub(1));
|
|
}
|
|
Ok(Resp::handled(resp.event))
|
|
} else {
|
|
// No active pane, don't handle
|
|
Err(event)
|
|
}
|
|
}
|
|
};
|
|
self.rescale();
|
|
res
|
|
}
|
|
}
|
|
|
|
impl Visual for Panes {
|
|
fn render(&mut self, state: &State, frame: &mut Rect) {
|
|
let n = self.hboxes.len();
|
|
if n == 0 {
|
|
return;
|
|
}
|
|
|
|
let total_weight = self.hboxes.iter().map(|h| h.size_weight).sum::<f32>();
|
|
|
|
self.last_area = frame.area();
|
|
|
|
let mut y0 = 0;
|
|
for (i, hbox) in self.hboxes.iter_mut().enumerate() {
|
|
let y1 = if i == n - 1 {
|
|
frame.size()[1]
|
|
} else {
|
|
y0 + ((hbox.size_weight * frame.size()[1] as f32 / total_weight)
|
|
.round()
|
|
.max(1.0) as usize)
|
|
.min(/*frame.size()[1] - (n - 1) * 3*/ !0)
|
|
};
|
|
|
|
// Draw pane contents
|
|
frame
|
|
.rect([0, y0], [frame.size()[0], y1.saturating_sub(y0)])
|
|
.with_focus(self.selected == i)
|
|
.with(|frame| hbox.render(state, frame));
|
|
|
|
y0 = y1;
|
|
}
|
|
}
|
|
}
|