div.rs

  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}