use std::marker::PhantomData; use gpui2::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size}; use smallvec::SmallVec; use crate::prelude::*; #[derive(Default, PartialEq)] pub enum SplitDirection { #[default] Horizontal, Vertical, } // #[derive(Element)] pub struct Pane { id: ElementId, state_type: PhantomData, size: Size, fill: Hsla, children: SmallVec<[AnyElement; 2]>, } impl IntoAnyElement for Pane { fn into_any(self) -> AnyElement { ElementRenderer { id: Some(self.id), render: Some(move |view_state, cx| self.render(view_state, cx)), view_type: PhantomData, element_type: PhantomData, } } } struct ElementRenderer where E: IntoAnyElement, F: FnOnce(&mut V, &mut ViewContext) -> E, { id: Option, render: Option, view_type: PhantomData, element_type: PhantomData, } impl Element for ElementRenderer where V: 'static, E: IntoAnyElement, F: FnOnce(&mut V, &mut ViewContext) -> E, { type ViewState = V; type ElementState = AnyElement; fn id(&self) -> Option { self.id } fn initialize( &mut self, view_state: &mut Self::ViewState, rendered_element: Option, cx: &mut ViewContext, ) -> Self::ElementState { rendered_element.unwrap_or_else(|| { let render = self.render.take().unwrap(); (render)(view_state, cx) }) } fn layout( &mut self, view_state: &mut Self::ViewState, rendered_element: &mut Self::ElementState, cx: &mut ViewContext, ) -> gpui2::LayoutId { rendered_element.layout(view_state, cx) } fn paint( &mut self, bounds: gpui2::Bounds, view_state: &mut Self::ViewState, rendered_element: &mut Self::ElementState, cx: &mut ViewContext, ) { rendered_element.paint(view_state, cx) } } impl IntoAnyElement for ElementRenderer where V: 'static, E: IntoAnyElement, F: FnOnce(&mut V, &mut ViewContext) -> E, { fn into_any(self) -> AnyElement { self } } impl Pane { pub fn new(id: impl Into, size: Size) -> Self { // Fill is only here for debugging purposes, remove before release Self { id: id.into(), state_type: PhantomData, size, fill: hsla(0.3, 0.3, 0.3, 1.), // fill: system_color.transparent, children: SmallVec::new(), } } pub fn fill(mut self, fill: Hsla) -> Self { self.fill = fill; self } fn render(&mut self, view: &mut S, cx: &mut ViewContext) -> impl Element { div() .id(self.id.clone()) .flex() .flex_initial() .bg(self.fill) .w(self.size.width) .h(self.size.height) .relative() .child( div() .z_index(0) .size_full() .children(self.children.drain(..)), ) .child( // TODO kb! Figure out why we can't we see the red background when we drag a file over this div. div() .z_index(1) .id("drag-target") .drag_over::(|d| d.bg(red())) .on_drop(|_, files: ExternalPaths, _| { dbg!("dropped files!", files); }) .absolute() .inset_0(), ) } } impl ParentElement for Pane { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { &mut self.children } } #[derive(Element)] pub struct PaneGroup { state_type: PhantomData, groups: Vec>, panes: Vec>, split_direction: SplitDirection, } impl PaneGroup { pub fn new_groups(groups: Vec>, split_direction: SplitDirection) -> Self { Self { state_type: PhantomData, groups, panes: Vec::new(), split_direction, } } pub fn new_panes(panes: Vec>, split_direction: SplitDirection) -> Self { Self { state_type: PhantomData, groups: Vec::new(), panes, split_direction, } } fn render(&mut self, view: &mut S, cx: &mut ViewContext) -> impl Element { let theme = theme(cx); if !self.panes.is_empty() { let el = div() .flex() .flex_1() .gap_px() .w_full() .h_full() .children(self.panes.iter_mut().map(|pane| pane.render(view, cx))); if self.split_direction == SplitDirection::Horizontal { return el; } else { return el.flex_col(); } } if !self.groups.is_empty() { let el = div() .flex() .flex_1() .gap_px() .w_full() .h_full() .bg(theme.editor) .children(self.groups.iter_mut().map(|group| group.render(view, cx))); if self.split_direction == SplitDirection::Horizontal { return el; } else { return el.flex_col(); } } unreachable!() } }