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