1use std::marker::PhantomData;
2
3use gpui3::AbsoluteLength;
4
5use crate::{prelude::*, theme};
6use crate::{token, v_stack};
7
8#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
9pub enum PanelAllowedSides {
10 LeftOnly,
11 RightOnly,
12 BottomOnly,
13 #[default]
14 LeftAndRight,
15 All,
16}
17
18impl PanelAllowedSides {
19 /// Return a `HashSet` that contains the allowable `PanelSide`s.
20 pub fn allowed_sides(&self) -> HashSet<PanelSide> {
21 match self {
22 Self::LeftOnly => HashSet::from_iter([PanelSide::Left]),
23 Self::RightOnly => HashSet::from_iter([PanelSide::Right]),
24 Self::BottomOnly => HashSet::from_iter([PanelSide::Bottom]),
25 Self::LeftAndRight => HashSet::from_iter([PanelSide::Left, PanelSide::Right]),
26 Self::All => HashSet::from_iter([PanelSide::Left, PanelSide::Right, PanelSide::Bottom]),
27 }
28 }
29}
30
31#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
32pub enum PanelSide {
33 #[default]
34 Left,
35 Right,
36 Bottom,
37}
38
39use std::collections::HashSet;
40
41#[derive(Element)]
42pub struct Panel<S: 'static + Send + Sync> {
43 state_type: PhantomData<S>,
44 scroll_state: ScrollState,
45 current_side: PanelSide,
46 /// Defaults to PanelAllowedSides::LeftAndRight
47 allowed_sides: PanelAllowedSides,
48 initial_width: AbsoluteLength,
49 width: Option<AbsoluteLength>,
50 children: HackyChildren<S>,
51 payload: HackyChildrenPayload,
52}
53
54impl<S: 'static + Send + Sync> Panel<S> {
55 pub fn new(
56 scroll_state: ScrollState,
57 children: HackyChildren<S>,
58 payload: HackyChildrenPayload,
59 ) -> Self {
60 let token = token();
61
62 Self {
63 state_type: PhantomData,
64 scroll_state,
65 current_side: PanelSide::default(),
66 allowed_sides: PanelAllowedSides::default(),
67 initial_width: token.default_panel_size,
68 width: None,
69 children,
70 payload,
71 }
72 }
73
74 pub fn initial_width(mut self, initial_width: AbsoluteLength) -> Self {
75 self.initial_width = initial_width;
76 self
77 }
78
79 pub fn width(mut self, width: AbsoluteLength) -> Self {
80 self.width = Some(width);
81 self
82 }
83
84 pub fn allowed_sides(mut self, allowed_sides: PanelAllowedSides) -> Self {
85 self.allowed_sides = allowed_sides;
86 self
87 }
88
89 pub fn side(mut self, side: PanelSide) -> Self {
90 let allowed_sides = self.allowed_sides.allowed_sides();
91
92 if allowed_sides.contains(&side) {
93 self.current_side = side;
94 } else {
95 panic!(
96 "The panel side {:?} was not added as allowed before it was set.",
97 side
98 );
99 }
100 self
101 }
102
103 fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
104 let token = token();
105 let theme = theme(cx);
106
107 let panel_base;
108 let current_width = self.width.unwrap_or(self.initial_width);
109
110 match self.current_side {
111 PanelSide::Left => {
112 panel_base = v_stack()
113 .flex_initial()
114 .h_full()
115 // .w(current_width)
116 .w_64()
117 .fill(theme.middle.base.default.background)
118 .border_r()
119 .border_color(theme.middle.base.default.border);
120 }
121 PanelSide::Right => {
122 panel_base = v_stack()
123 .flex_initial()
124 .h_full()
125 // .w(current_width)
126 .w_64()
127 .fill(theme.middle.base.default.background)
128 .border_l()
129 .border_color(theme.middle.base.default.border);
130 }
131 PanelSide::Bottom => {
132 panel_base = v_stack()
133 .flex_initial()
134 .w_full()
135 // .h(current_width)
136 .h_64()
137 .fill(theme.middle.base.default.background)
138 .border_t()
139 .border_color(theme.middle.base.default.border);
140 }
141 }
142
143 panel_base.children_any((self.children)(cx, self.payload.as_ref()))
144 }
145}
146
147#[cfg(feature = "stories")]
148pub use stories::*;
149
150#[cfg(feature = "stories")]
151mod stories {
152 use crate::{Label, Story};
153
154 use super::*;
155
156 #[derive(Element)]
157 pub struct PanelStory<S: 'static + Send + Sync + Clone> {
158 state_type: PhantomData<S>,
159 }
160
161 impl<S: 'static + Send + Sync + Clone> PanelStory<S> {
162 pub fn new() -> Self {
163 Self {
164 state_type: PhantomData,
165 }
166 }
167
168 fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
169 Story::container(cx)
170 .child(Story::title_for::<_, Panel<S>>(cx))
171 .child(Story::label(cx, "Default"))
172 .child(Panel::new(
173 ScrollState::default(),
174 |_, _| {
175 vec![div()
176 .overflow_y_scroll(ScrollState::default())
177 .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1))))
178 .into_any()]
179 },
180 Box::new(()),
181 ))
182 }
183 }
184}