1use gpui::{
2 hsla, red, AnyElement, Div, ElementId, ExternalPaths, Hsla, Length, RenderOnce, Size, Stateful,
3 View,
4};
5use smallvec::SmallVec;
6
7use crate::prelude::*;
8
9#[derive(Default, PartialEq)]
10pub enum SplitDirection {
11 #[default]
12 Horizontal,
13 Vertical,
14}
15
16#[derive(RenderOnce)]
17pub struct Pane<V: 'static> {
18 id: ElementId,
19 size: Size<Length>,
20 fill: Hsla,
21 children: SmallVec<[AnyElement<V>; 2]>,
22}
23
24impl<V: 'static> Component<V> for Pane<V> {
25 type Rendered = Stateful<V, Div<V>>;
26
27 fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
28 div()
29 .id(self.id.clone())
30 .flex()
31 .flex_initial()
32 .bg(self.fill)
33 .w(self.size.width)
34 .h(self.size.height)
35 .relative()
36 .child(div().z_index(0).size_full().children(self.children))
37 .child(
38 div()
39 .z_index(1)
40 .id("drag-target")
41 .drag_over::<ExternalPaths>(|d| d.bg(red()))
42 .on_drop(|_, files: View<ExternalPaths>, cx| {
43 eprintln!("dropped files! {:?}", files.read(cx));
44 })
45 .absolute()
46 .inset_0(),
47 )
48 }
49}
50
51impl<V: 'static> Pane<V> {
52 pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
53 // Fill is only here for debugging purposes, remove before release
54
55 Self {
56 id: id.into(),
57 size,
58 fill: hsla(0.3, 0.3, 0.3, 1.),
59 children: SmallVec::new(),
60 }
61 }
62
63 pub fn fill(mut self, fill: Hsla) -> Self {
64 self.fill = fill;
65 self
66 }
67}
68
69impl<V: 'static> ParentElement<V> for Pane<V> {
70 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
71 &mut self.children
72 }
73}
74
75#[derive(RenderOnce)]
76pub struct PaneGroup<V: 'static> {
77 groups: Vec<PaneGroup<V>>,
78 panes: Vec<Pane<V>>,
79 split_direction: SplitDirection,
80}
81
82impl<V: 'static> Component<V> for PaneGroup<V> {
83 type Rendered = Div<V>;
84
85 fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
86 if !self.panes.is_empty() {
87 let el = div()
88 .flex()
89 .flex_1()
90 .gap_px()
91 .w_full()
92 .h_full()
93 .children(self.panes.into_iter().map(|pane| pane.render(view, cx)));
94
95 if self.split_direction == SplitDirection::Horizontal {
96 return el;
97 } else {
98 return el.flex_col();
99 }
100 }
101
102 if !self.groups.is_empty() {
103 let el = div()
104 .flex()
105 .flex_1()
106 .gap_px()
107 .w_full()
108 .h_full()
109 .bg(cx.theme().colors().editor_background)
110 .children(self.groups.into_iter().map(|group| group.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 unreachable!()
120 }
121}
122
123impl<V: 'static> PaneGroup<V> {
124 pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
125 Self {
126 groups,
127 panes: Vec::new(),
128 split_direction,
129 }
130 }
131
132 pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
133 Self {
134 groups: Vec::new(),
135 panes,
136 split_direction,
137 }
138 }
139}