panes.rs

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