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<V: 'static + Send + Sync> IntoAnyElement<V> for Pane<V> {
25 fn into_any(self) -> AnyElement<V> {
26 ElementRenderer {
27 id: Some(self.id),
28 render: Some(move |view_state, cx| self.render(view_state, cx)),
29 view_type: PhantomData,
30 element_type: PhantomData,
31 }
32 }
33}
34
35struct ElementRenderer<V, E, F>
36where
37 E: IntoAnyElement<V>,
38 F: FnOnce(&mut V, &mut ViewContext<V>) -> E,
39{
40 id: Option<ElementId>,
41 render: Option<F>,
42 view_type: PhantomData<V>,
43 element_type: PhantomData<E>,
44}
45
46impl<V, E, F> Element for ElementRenderer<V, E, F>
47where
48 V: 'static,
49 E: IntoAnyElement<V>,
50 F: FnOnce(&mut V, &mut ViewContext<V>) -> E,
51{
52 type ViewState = V;
53 type ElementState = AnyElement<V>;
54
55 fn id(&self) -> Option<ElementId> {
56 self.id
57 }
58
59 fn initialize(
60 &mut self,
61 view_state: &mut Self::ViewState,
62 rendered_element: Option<Self::ElementState>,
63 cx: &mut ViewContext<Self::ViewState>,
64 ) -> Self::ElementState {
65 rendered_element.unwrap_or_else(|| {
66 let render = self.render.take().unwrap();
67 (render)(view_state, cx)
68 })
69 }
70
71 fn layout(
72 &mut self,
73 view_state: &mut Self::ViewState,
74 rendered_element: &mut Self::ElementState,
75 cx: &mut ViewContext<Self::ViewState>,
76 ) -> gpui2::LayoutId {
77 rendered_element.layout(view_state, cx)
78 }
79
80 fn paint(
81 &mut self,
82 bounds: gpui2::Bounds<gpui2::Pixels>,
83 view_state: &mut Self::ViewState,
84 rendered_element: &mut Self::ElementState,
85 cx: &mut ViewContext<Self::ViewState>,
86 ) {
87 rendered_element.paint(view_state, cx)
88 }
89}
90
91impl<V, E, F> IntoAnyElement<V> for ElementRenderer<V, E, F>
92where
93 V: 'static,
94 E: IntoAnyElement<V>,
95 F: FnOnce(&mut V, &mut ViewContext<V>) -> E,
96{
97 fn into_any(self) -> AnyElement<V> {
98 self
99 }
100}
101
102impl<S: 'static + Send + Sync> Pane<S> {
103 pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
104 // Fill is only here for debugging purposes, remove before release
105
106 Self {
107 id: id.into(),
108 state_type: PhantomData,
109 size,
110 fill: hsla(0.3, 0.3, 0.3, 1.),
111 // fill: system_color.transparent,
112 children: SmallVec::new(),
113 }
114 }
115
116 pub fn fill(mut self, fill: Hsla) -> Self {
117 self.fill = fill;
118 self
119 }
120
121 fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<S> {
122 div()
123 .id(self.id.clone())
124 .flex()
125 .flex_initial()
126 .bg(self.fill)
127 .w(self.size.width)
128 .h(self.size.height)
129 .relative()
130 .child(
131 div()
132 .z_index(0)
133 .size_full()
134 .children(self.children.drain(..)),
135 )
136 .child(
137 // TODO kb! Figure out why we can't we see the red background when we drag a file over this div.
138 div()
139 .z_index(1)
140 .id("drag-target")
141 .drag_over::<ExternalPaths>(|d| d.bg(red()))
142 .on_drop(|_, files: ExternalPaths, _| {
143 dbg!("dropped files!", files);
144 })
145 .absolute()
146 .inset_0(),
147 )
148 }
149}
150
151impl<S: 'static + Send + Sync> ParentElement<S> for Pane<S> {
152 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]> {
153 &mut self.children
154 }
155}
156
157#[derive(Element)]
158pub struct PaneGroup<S: 'static + Send + Sync> {
159 state_type: PhantomData<S>,
160 groups: Vec<PaneGroup<S>>,
161 panes: Vec<Pane<S>>,
162 split_direction: SplitDirection,
163}
164
165impl<S: 'static + Send + Sync> PaneGroup<S> {
166 pub fn new_groups(groups: Vec<PaneGroup<S>>, split_direction: SplitDirection) -> Self {
167 Self {
168 state_type: PhantomData,
169 groups,
170 panes: Vec::new(),
171 split_direction,
172 }
173 }
174
175 pub fn new_panes(panes: Vec<Pane<S>>, split_direction: SplitDirection) -> Self {
176 Self {
177 state_type: PhantomData,
178 groups: Vec::new(),
179 panes,
180 split_direction,
181 }
182 }
183
184 fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<S> {
185 let theme = theme(cx);
186
187 if !self.panes.is_empty() {
188 let el = div()
189 .flex()
190 .flex_1()
191 .gap_px()
192 .w_full()
193 .h_full()
194 .children(self.panes.iter_mut().map(|pane| pane.render(view, cx)));
195
196 if self.split_direction == SplitDirection::Horizontal {
197 return el;
198 } else {
199 return el.flex_col();
200 }
201 }
202
203 if !self.groups.is_empty() {
204 let el = div()
205 .flex()
206 .flex_1()
207 .gap_px()
208 .w_full()
209 .h_full()
210 .bg(theme.editor)
211 .children(self.groups.iter_mut().map(|group| group.render(view, cx)));
212
213 if self.split_direction == SplitDirection::Horizontal {
214 return el;
215 } else {
216 return el.flex_col();
217 }
218 }
219
220 unreachable!()
221 }
222}