1use std::{cell::Cell, rc::Rc};
2
3use crate::{
4 element::{AnyElement, Element, IntoElement, Layout, ParentElement},
5 hsla,
6 paint_context::PaintContext,
7 style::{CornerRadii, Overflow, Style, StyleHelpers, Styleable},
8 InteractionHandlers, Interactive, ViewContext,
9};
10use anyhow::Result;
11use gpui::{
12 geometry::{rect::RectF, vector::Vector2F, Point},
13 platform::{MouseButton, MouseButtonEvent, MouseMovedEvent, ScrollWheelEvent},
14 scene::{self},
15 LayoutId,
16};
17use refineable::{Refineable, RefinementCascade};
18use smallvec::SmallVec;
19use util::ResultExt;
20
21pub struct Div<V: 'static> {
22 styles: RefinementCascade<Style>,
23 handlers: InteractionHandlers<V>,
24 children: SmallVec<[AnyElement<V>; 2]>,
25 scroll_state: Option<ScrollState>,
26}
27
28pub fn div<V>() -> Div<V> {
29 Div {
30 styles: Default::default(),
31 handlers: Default::default(),
32 children: Default::default(),
33 scroll_state: None,
34 }
35}
36
37impl<V: 'static> Element<V> for Div<V> {
38 type PaintState = Vec<LayoutId>;
39
40 fn layout(
41 &mut self,
42 view: &mut V,
43 cx: &mut ViewContext<V>,
44 ) -> Result<(LayoutId, Self::PaintState)>
45 where
46 Self: Sized,
47 {
48 let style = self.computed_style();
49 let pop_text_style = style.text_style(cx).map_or(false, |style| {
50 cx.push_text_style(&style).log_err().is_some()
51 });
52
53 let children = self
54 .children
55 .iter_mut()
56 .map(|child| child.layout(view, cx))
57 .collect::<Result<Vec<LayoutId>>>()?;
58
59 if pop_text_style {
60 cx.pop_text_style();
61 }
62
63 Ok((cx.add_layout_node(style, children.clone())?, children))
64 }
65
66 fn paint(
67 &mut self,
68 view: &mut V,
69 parent_origin: Vector2F,
70 layout: &Layout,
71 child_layouts: &mut Vec<LayoutId>,
72 cx: &mut PaintContext<V>,
73 ) where
74 Self: Sized,
75 {
76 let order = layout.order;
77 let bounds = layout.bounds + parent_origin;
78
79 let style = self.computed_style();
80 let pop_text_style = style.text_style(cx).map_or(false, |style| {
81 cx.push_text_style(&style).log_err().is_some()
82 });
83 style.paint_background(bounds, cx);
84 self.interaction_handlers().paint(order, bounds, cx);
85
86 let scrolled_origin = bounds.origin() - self.scroll_offset(&style.overflow);
87
88 // TODO: Support only one dimension being hidden
89 let mut pop_layer = false;
90 if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible {
91 cx.scene().push_layer(Some(bounds));
92 pop_layer = true;
93 }
94
95 for child in &mut self.children {
96 child.paint(view, scrolled_origin, cx);
97 }
98
99 if pop_layer {
100 cx.scene().pop_layer();
101 }
102
103 style.paint_foreground(bounds, cx);
104 if pop_text_style {
105 cx.pop_text_style();
106 }
107
108 self.handle_scroll(order, bounds, style.overflow.clone(), child_layouts, cx);
109
110 if cx.is_inspector_enabled() {
111 self.paint_inspector(parent_origin, layout, cx);
112 }
113 }
114}
115
116impl<V: 'static> Div<V> {
117 pub fn overflow_hidden(mut self) -> Self {
118 self.declared_style().overflow.x = Some(Overflow::Hidden);
119 self.declared_style().overflow.y = Some(Overflow::Hidden);
120 self
121 }
122
123 pub fn overflow_hidden_x(mut self) -> Self {
124 self.declared_style().overflow.x = Some(Overflow::Hidden);
125 self
126 }
127
128 pub fn overflow_hidden_y(mut self) -> Self {
129 self.declared_style().overflow.y = Some(Overflow::Hidden);
130 self
131 }
132
133 pub fn overflow_scroll(mut self, scroll_state: ScrollState) -> Self {
134 self.scroll_state = Some(scroll_state);
135 self.declared_style().overflow.x = Some(Overflow::Scroll);
136 self.declared_style().overflow.y = Some(Overflow::Scroll);
137 self
138 }
139
140 pub fn overflow_x_scroll(mut self, scroll_state: ScrollState) -> Self {
141 self.scroll_state = Some(scroll_state);
142 self.declared_style().overflow.x = Some(Overflow::Scroll);
143 self
144 }
145
146 pub fn overflow_y_scroll(mut self, scroll_state: ScrollState) -> Self {
147 self.scroll_state = Some(scroll_state);
148 self.declared_style().overflow.y = Some(Overflow::Scroll);
149 self
150 }
151
152 fn scroll_offset(&self, overflow: &Point<Overflow>) -> Vector2F {
153 let mut offset = Vector2F::zero();
154 if overflow.y == Overflow::Scroll {
155 offset.set_y(self.scroll_state.as_ref().unwrap().y());
156 }
157 if overflow.x == Overflow::Scroll {
158 offset.set_x(self.scroll_state.as_ref().unwrap().x());
159 }
160
161 offset
162 }
163
164 fn handle_scroll(
165 &mut self,
166 order: u32,
167 bounds: RectF,
168 overflow: Point<Overflow>,
169 child_layout_ids: &[LayoutId],
170 cx: &mut PaintContext<V>,
171 ) {
172 if overflow.y == Overflow::Scroll || overflow.x == Overflow::Scroll {
173 let mut scroll_max = Vector2F::zero();
174 for child_layout_id in child_layout_ids {
175 if let Some(child_layout) = cx
176 .layout_engine()
177 .unwrap()
178 .computed_layout(*child_layout_id)
179 .log_err()
180 {
181 scroll_max = scroll_max.max(child_layout.bounds.lower_right());
182 }
183 }
184 scroll_max -= bounds.size();
185
186 let scroll_state = self.scroll_state.as_ref().unwrap().clone();
187 cx.on_event(order, move |_, event: &ScrollWheelEvent, cx| {
188 if bounds.contains_point(event.position) {
189 let scroll_delta = match event.delta {
190 gpui::platform::ScrollDelta::Pixels(delta) => delta,
191 gpui::platform::ScrollDelta::Lines(delta) => {
192 delta * cx.text_style().font_size
193 }
194 };
195 if overflow.x == Overflow::Scroll {
196 scroll_state.set_x(
197 (scroll_state.x() - scroll_delta.x())
198 .max(0.)
199 .min(scroll_max.x()),
200 );
201 }
202 if overflow.y == Overflow::Scroll {
203 scroll_state.set_y(
204 (scroll_state.y() - scroll_delta.y())
205 .max(0.)
206 .min(scroll_max.y()),
207 );
208 }
209 cx.repaint();
210 } else {
211 cx.bubble_event();
212 }
213 })
214 }
215 }
216
217 fn paint_inspector(&self, parent_origin: Vector2F, layout: &Layout, cx: &mut PaintContext<V>) {
218 let style = self.styles.merged();
219 let bounds = layout.bounds + parent_origin;
220
221 let hovered = bounds.contains_point(cx.mouse_position());
222 if hovered {
223 let rem_size = cx.rem_size();
224 cx.scene().push_quad(scene::Quad {
225 bounds,
226 background: Some(hsla(0., 0., 1., 0.05).into()),
227 border: gpui::Border {
228 color: hsla(0., 0., 1., 0.2).into(),
229 top: 1.,
230 right: 1.,
231 bottom: 1.,
232 left: 1.,
233 },
234 corner_radii: CornerRadii::default()
235 .refined(&style.corner_radii)
236 .to_gpui(bounds.size(), rem_size),
237 })
238 }
239
240 let pressed = Cell::new(hovered && cx.is_mouse_down(MouseButton::Left));
241 cx.on_event(layout.order, move |_, event: &MouseButtonEvent, _| {
242 if bounds.contains_point(event.position) {
243 if event.is_down {
244 pressed.set(true);
245 } else if pressed.get() {
246 pressed.set(false);
247 eprintln!("clicked div {:?} {:#?}", bounds, style);
248 }
249 }
250 });
251
252 let hovered = Cell::new(hovered);
253 cx.on_event(layout.order, move |_, event: &MouseMovedEvent, cx| {
254 cx.bubble_event();
255 let hovered_now = bounds.contains_point(event.position);
256 if hovered.get() != hovered_now {
257 hovered.set(hovered_now);
258 cx.repaint();
259 }
260 });
261 }
262}
263
264impl<V> Styleable for Div<V> {
265 type Style = Style;
266
267 fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
268 &mut self.styles
269 }
270
271 fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
272 self.styles.base()
273 }
274}
275
276impl<V> StyleHelpers for Div<V> {}
277
278impl<V> Interactive<V> for Div<V> {
279 fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
280 &mut self.handlers
281 }
282}
283
284impl<V: 'static> ParentElement<V> for Div<V> {
285 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
286 &mut self.children
287 }
288}
289
290impl<V: 'static> IntoElement<V> for Div<V> {
291 type Element = Self;
292
293 fn into_element(self) -> Self::Element {
294 self
295 }
296}
297
298#[derive(Default, Clone)]
299pub struct ScrollState(Rc<Cell<Vector2F>>);
300
301impl ScrollState {
302 pub fn x(&self) -> f32 {
303 self.0.get().x()
304 }
305
306 pub fn set_x(&self, value: f32) {
307 let mut current_value = self.0.get();
308 current_value.set_x(value);
309 self.0.set(current_value);
310 }
311
312 pub fn y(&self) -> f32 {
313 self.0.get().y()
314 }
315
316 pub fn set_y(&self, value: f32) {
317 let mut current_value = self.0.get();
318 current_value.set_y(value);
319 self.0.set(current_value);
320 }
321}