panes.rs

  1use std::marker::PhantomData;
  2
  3use gpui2::{hsla, red, AnyElement, DroppedFiles, ElementId, Hsla, Length, Size};
  4use smallvec::SmallVec;
  5
  6use crate::prelude::*;
  7
  8#[derive(Default, PartialEq)]
  9pub enum SplitDirection {
 10    #[default]
 11    Horizontal,
 12    Vertical,
 13}
 14
 15#[derive(Element)]
 16pub struct Pane<S: 'static + Send + Sync> {
 17    id: ElementId,
 18    state_type: PhantomData<S>,
 19    size: Size<Length>,
 20    fill: Hsla,
 21    children: SmallVec<[AnyElement<S>; 2]>,
 22}
 23
 24impl<S: 'static + Send + Sync> Pane<S> {
 25    pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
 26        // Fill is only here for debugging purposes, remove before release
 27
 28        Self {
 29            id: id.into(),
 30            state_type: PhantomData,
 31            size,
 32            fill: hsla(0.3, 0.3, 0.3, 1.),
 33            // fill: system_color.transparent,
 34            children: SmallVec::new(),
 35        }
 36    }
 37
 38    pub fn fill(mut self, fill: Hsla) -> Self {
 39        self.fill = fill;
 40        self
 41    }
 42
 43    fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 44        div()
 45            .id(self.id.clone())
 46            .flex()
 47            .flex_initial()
 48            .bg(self.fill)
 49            .w(self.size.width)
 50            .h(self.size.height)
 51            .relative()
 52            .children(cx.stack(0, |_| self.children.drain(..)))
 53            .child(cx.stack(1, |_| {
 54                // TODO kb! Figure out why we can't we see the red background when we drag a file over this div.
 55                div()
 56                    .id("drag-target")
 57                    .drag_over::<DroppedFiles>(|d| d.bg(red()))
 58                    .on_drop(|_, files: DroppedFiles, _| {
 59                        dbg!("dropped files!", files);
 60                    })
 61                    .absolute()
 62                    .inset_0()
 63            }))
 64    }
 65}
 66
 67impl<S: 'static + Send + Sync> ParentElement for Pane<S> {
 68    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
 69        &mut self.children
 70    }
 71}
 72
 73#[derive(Element)]
 74pub struct PaneGroup<S: 'static + Send + Sync> {
 75    state_type: PhantomData<S>,
 76    groups: Vec<PaneGroup<S>>,
 77    panes: Vec<Pane<S>>,
 78    split_direction: SplitDirection,
 79}
 80
 81impl<S: 'static + Send + Sync> PaneGroup<S> {
 82    pub fn new_groups(groups: Vec<PaneGroup<S>>, split_direction: SplitDirection) -> Self {
 83        Self {
 84            state_type: PhantomData,
 85            groups,
 86            panes: Vec::new(),
 87            split_direction,
 88        }
 89    }
 90
 91    pub fn new_panes(panes: Vec<Pane<S>>, split_direction: SplitDirection) -> Self {
 92        Self {
 93            state_type: PhantomData,
 94            groups: Vec::new(),
 95            panes,
 96            split_direction,
 97        }
 98    }
 99
100    fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
101        let theme = theme(cx);
102
103        if !self.panes.is_empty() {
104            let el = div()
105                .flex()
106                .flex_1()
107                .gap_px()
108                .w_full()
109                .h_full()
110                .children(self.panes.iter_mut().map(|pane| pane.render(view, cx)));
111
112            if self.split_direction == SplitDirection::Horizontal {
113                return el;
114            } else {
115                return el.flex_col();
116            }
117        }
118
119        if !self.groups.is_empty() {
120            let el = div()
121                .flex()
122                .flex_1()
123                .gap_px()
124                .w_full()
125                .h_full()
126                .bg(theme.editor)
127                .children(self.groups.iter_mut().map(|group| group.render(view, cx)));
128
129            if self.split_direction == SplitDirection::Horizontal {
130                return el;
131            } else {
132                return el.flex_col();
133            }
134        }
135
136        unreachable!()
137    }
138}