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        let color = ThemeColor::new(cx);
 45
 46        div()
 47            .id(self.id.clone())
 48            .flex()
 49            .flex_initial()
 50            .bg(self.fill)
 51            .w(self.size.width)
 52            .h(self.size.height)
 53            .relative()
 54            .children(cx.stack(0, |_| self.children.drain(..)))
 55            .child(cx.stack(1, |_| {
 56                // TODO kb! Figure out why we can't we see the red background when we drag a file over this div.
 57                div()
 58                    .id("drag-target")
 59                    .drag_over::<DroppedFiles>(|d| d.bg(red()))
 60                    .on_drop(|_, files: DroppedFiles, _| {
 61                        dbg!("dropped files!", files);
 62                    })
 63                    .absolute()
 64                    .inset_0()
 65            }))
 66    }
 67}
 68
 69impl<S: 'static + Send + Sync> ParentElement for Pane<S> {
 70    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
 71        &mut self.children
 72    }
 73}
 74
 75#[derive(Element)]
 76pub struct PaneGroup<S: 'static + Send + Sync> {
 77    state_type: PhantomData<S>,
 78    groups: Vec<PaneGroup<S>>,
 79    panes: Vec<Pane<S>>,
 80    split_direction: SplitDirection,
 81}
 82
 83impl<S: 'static + Send + Sync> PaneGroup<S> {
 84    pub fn new_groups(groups: Vec<PaneGroup<S>>, split_direction: SplitDirection) -> Self {
 85        Self {
 86            state_type: PhantomData,
 87            groups,
 88            panes: Vec::new(),
 89            split_direction,
 90        }
 91    }
 92
 93    pub fn new_panes(panes: Vec<Pane<S>>, split_direction: SplitDirection) -> Self {
 94        Self {
 95            state_type: PhantomData,
 96            groups: Vec::new(),
 97            panes,
 98            split_direction,
 99        }
100    }
101
102    fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
103        let color = ThemeColor::new(cx);
104
105        if !self.panes.is_empty() {
106            let el = div()
107                .flex()
108                .flex_1()
109                .gap_px()
110                .w_full()
111                .h_full()
112                .children(self.panes.iter_mut().map(|pane| pane.render(view, cx)));
113
114            if self.split_direction == SplitDirection::Horizontal {
115                return el;
116            } else {
117                return el.flex_col();
118            }
119        }
120
121        if !self.groups.is_empty() {
122            let el = div()
123                .flex()
124                .flex_1()
125                .gap_px()
126                .w_full()
127                .h_full()
128                .bg(color.editor)
129                .children(self.groups.iter_mut().map(|group| group.render(view, cx)));
130
131            if self.split_direction == SplitDirection::Horizontal {
132                return el;
133            } else {
134                return el.flex_col();
135            }
136        }
137
138        unreachable!()
139    }
140}