div.rs

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