div.rs

  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}