div.rs

  1use std::cell::Cell;
  2
  3use crate::{
  4    element::{AnyElement, Element, IntoElement, Layout, ParentElement},
  5    hsla,
  6    layout_context::LayoutContext,
  7    paint_context::PaintContext,
  8    style::{CornerRadii, Style, StyleHelpers, Styleable},
  9    InteractionHandlers, Interactive,
 10};
 11use anyhow::Result;
 12use gpui::{
 13    platform::{MouseButton, MouseButtonEvent, MouseMovedEvent},
 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}
 26
 27pub fn div<V>() -> Div<V> {
 28    Div {
 29        styles: Default::default(),
 30        handlers: Default::default(),
 31        children: Default::default(),
 32    }
 33}
 34
 35impl<V: 'static> Element<V> for Div<V> {
 36    type PaintState = ();
 37
 38    fn layout(
 39        &mut self,
 40        view: &mut V,
 41        cx: &mut LayoutContext<V>,
 42    ) -> Result<(LayoutId, Self::PaintState)>
 43    where
 44        Self: Sized,
 45    {
 46        let style = self.computed_style();
 47        let pop_text_style = style.text_style(cx).map_or(false, |style| {
 48            cx.push_text_style(&style).log_err().is_some()
 49        });
 50
 51        let children = self
 52            .children
 53            .iter_mut()
 54            .map(|child| child.layout(view, cx))
 55            .collect::<Result<Vec<LayoutId>>>()?;
 56
 57        if pop_text_style {
 58            cx.pop_text_style();
 59        }
 60
 61        Ok((cx.add_layout_node(style, children)?, ()))
 62    }
 63
 64    fn paint(
 65        &mut self,
 66        view: &mut V,
 67        layout: &Layout,
 68        _: &mut Self::PaintState,
 69        cx: &mut PaintContext<V>,
 70    ) where
 71        Self: Sized,
 72    {
 73        let style = self.computed_style();
 74        let pop_text_style = style.text_style(cx).map_or(false, |style| {
 75            cx.push_text_style(&style).log_err().is_some()
 76        });
 77        style.paint_background(layout.bounds, cx);
 78        self.interaction_handlers()
 79            .paint(layout.order, layout.bounds, cx);
 80        for child in &mut self.children {
 81            child.paint(view, layout.bounds.origin(), cx);
 82        }
 83        style.paint_foreground(layout.bounds, cx);
 84        if pop_text_style {
 85            cx.pop_text_style();
 86        }
 87
 88        if cx.is_inspector_enabled() {
 89            self.paint_inspector(layout, cx);
 90        }
 91    }
 92}
 93
 94impl<V: 'static> Div<V> {
 95    fn paint_inspector(&self, layout: &Layout, cx: &mut PaintContext<V>) {
 96        let style = self.styles.merged();
 97
 98        let hovered = layout.bounds.contains_point(cx.mouse_position());
 99        if hovered {
100            let rem_size = cx.rem_size();
101            cx.scene.push_quad(scene::Quad {
102                bounds: layout.bounds,
103                background: Some(hsla(0., 0., 1., 0.05).into()),
104                border: gpui::Border {
105                    color: hsla(0., 0., 1., 0.2).into(),
106                    top: 1.,
107                    right: 1.,
108                    bottom: 1.,
109                    left: 1.,
110                },
111                corner_radii: CornerRadii::default()
112                    .refined(&style.corner_radii)
113                    .to_gpui(layout.bounds.size(), rem_size),
114            })
115        }
116
117        let bounds = layout.bounds;
118        let pressed = Cell::new(hovered && cx.is_mouse_down(MouseButton::Left));
119        cx.on_event(layout.order, move |_, event: &MouseButtonEvent, _| {
120            if bounds.contains_point(event.position) {
121                if event.is_down {
122                    pressed.set(true);
123                } else if pressed.get() {
124                    pressed.set(false);
125                    eprintln!("clicked div {:?} {:#?}", bounds, style);
126                }
127            }
128        });
129
130        let hovered = Cell::new(hovered);
131        cx.on_event(layout.order, move |_, event: &MouseMovedEvent, cx| {
132            cx.bubble_event();
133            let hovered_now = bounds.contains_point(event.position);
134            if hovered.get() != hovered_now {
135                hovered.set(hovered_now);
136                cx.repaint();
137            }
138        });
139    }
140}
141
142impl<V> Styleable for Div<V> {
143    type Style = Style;
144
145    fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
146        &mut self.styles
147    }
148
149    fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
150        self.styles.base()
151    }
152}
153
154impl<V> StyleHelpers for Div<V> {}
155
156impl<V> Interactive<V> for Div<V> {
157    fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
158        &mut self.handlers
159    }
160}
161
162impl<V: 'static> ParentElement<V> for Div<V> {
163    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
164        &mut self.children
165    }
166}
167
168impl<V: 'static> IntoElement<V> for Div<V> {
169    type Element = Self;
170
171    fn into_element(self) -> Self::Element {
172        self
173    }
174}