mod divider;
mod lifecycle;
mod preview;
mod render;
mod tree;
use std::cell::Cell;
use std::rc::Rc;
use gpui::{Axis, Bounds, Entity, FocusHandle, Focusable, Subscription};
pub use preview::PaneGroupPreview;
use crate::{Pane, prelude::*};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SplitDirection {
Left,
Right,
Up,
Down,
}
impl SplitDirection {
pub fn axis(self) -> Axis {
match self {
SplitDirection::Left | SplitDirection::Right => Axis::Horizontal,
SplitDirection::Up | SplitDirection::Down => Axis::Vertical,
}
}
fn inserts_before(self) -> bool {
matches!(self, SplitDirection::Left | SplitDirection::Up)
}
}
pub enum Member {
Leaf(Entity<Pane>),
Split(PaneAxis),
}
pub struct PaneAxis {
pub axis: Axis,
pub members: Vec<Member>,
pub flexes: Vec<f32>,
bounds: Rc<Cell<Option<Bounds<Pixels>>>>,
}
impl PaneAxis {
fn new(axis: Axis, members: Vec<Member>, flexes: Vec<f32>) -> Self {
Self {
axis,
members,
flexes,
bounds: Rc::new(Cell::new(None)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CannotRemoveLastPane;
pub struct PaneGroup {
root: Member,
active_pane: Entity<Pane>,
pane_factory: Box<dyn Fn(&mut Context<Pane>) -> Pane>,
focus_handle: FocusHandle,
_subscriptions: Vec<Subscription>,
}
impl PaneGroup {
pub fn new(cx: &mut Context<Self>, pane: Entity<Pane>) -> Self {
let subscription = Self::watch_pane(&pane, cx);
Self {
root: Member::Leaf(pane.clone()),
active_pane: pane,
pane_factory: Box::new(|_| Pane::new()),
focus_handle: cx.focus_handle(),
_subscriptions: vec![subscription],
}
}
pub fn with_pane_factory(
mut self,
factory: impl Fn(&mut Context<Pane>) -> Pane + 'static,
) -> Self {
self.pane_factory = Box::new(factory);
self
}
pub fn active_pane(&self) -> &Entity<Pane> {
&self.active_pane
}
pub fn set_active_pane(&mut self, pane: Entity<Pane>, cx: &mut Context<Self>) {
self.active_pane = pane;
cx.notify();
}
pub fn split(&mut self, dir: SplitDirection, cx: &mut Context<Self>) {
let new_pane = cx.new(|pane_cx| (self.pane_factory)(pane_cx));
let subscription = Self::watch_pane(&new_pane, cx);
self._subscriptions.push(subscription);
let active_id = self.active_pane.entity_id();
let inserted = self.root.split_active(
active_id,
dir.axis(),
dir.inserts_before(),
new_pane.clone(),
);
debug_assert!(
inserted,
"PaneGroup's active pane must exist in its own tree"
);
self.active_pane = new_pane;
cx.notify();
}
pub fn close_active(&mut self, cx: &mut Context<Self>) -> Result<(), CannotRemoveLastPane> {
let target = self.active_pane.clone();
self.remove_pane(&target, cx)
}
pub fn focus(&mut self, dir: SplitDirection, cx: &mut Context<Self>) {
let active_id = self.active_pane.entity_id();
let neighbor = match &self.root {
Member::Leaf(_) => None,
Member::Split(axis) => axis.find_neighbor(active_id, dir),
};
if let Some(neighbor) = neighbor {
self.active_pane = neighbor;
cx.notify();
}
}
}
impl Focusable for PaneGroup {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
}
}