div.rs

  1use crate::{
  2    Active, AnonymousElement, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase,
  3    Element, ElementId, ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement,
  4    LayoutId, MouseClickEvent, MouseDownEvent, MouseEventListeners, MouseMoveEvent, MouseUpEvent,
  5    Overflow, ParentElement, Pixels, Point, ScrollWheelEvent, SharedString, Style, StyleRefinement,
  6    Styled, ViewContext,
  7};
  8use collections::HashMap;
  9use parking_lot::Mutex;
 10use refineable::Refineable;
 11use smallvec::SmallVec;
 12use std::sync::Arc;
 13
 14#[derive(Default)]
 15pub struct DivState {
 16    active_state: Arc<Mutex<ActiveState>>,
 17    pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
 18}
 19
 20#[derive(Copy, Clone, Default, Eq, PartialEq)]
 21struct ActiveState {
 22    group: bool,
 23    element: bool,
 24}
 25
 26impl ActiveState {
 27    pub fn is_none(&self) -> bool {
 28        !self.group && !self.element
 29    }
 30}
 31
 32#[derive(Default)]
 33struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
 34
 35pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
 36    cx.default_global::<GroupBounds>()
 37        .0
 38        .get(name)
 39        .and_then(|bounds_stack| bounds_stack.last().cloned())
 40}
 41
 42#[derive(Default, Clone)]
 43pub struct ScrollState(Arc<Mutex<Point<Pixels>>>);
 44
 45impl ScrollState {
 46    pub fn x(&self) -> Pixels {
 47        self.0.lock().x
 48    }
 49
 50    pub fn set_x(&self, value: Pixels) {
 51        self.0.lock().x = value;
 52    }
 53
 54    pub fn y(&self) -> Pixels {
 55        self.0.lock().y
 56    }
 57
 58    pub fn set_y(&self, value: Pixels) {
 59        self.0.lock().y = value;
 60    }
 61}
 62
 63pub fn div<S>() -> Div<S, AnonymousElement>
 64where
 65    S: 'static + Send + Sync,
 66{
 67    Div {
 68        kind: AnonymousElement,
 69        children: SmallVec::new(),
 70        group: None,
 71        base_style: StyleRefinement::default(),
 72        hover_style: StyleRefinement::default(),
 73        group_hover: None,
 74        active_style: StyleRefinement::default(),
 75        group_active: None,
 76        listeners: MouseEventListeners::default(),
 77    }
 78}
 79
 80pub struct Div<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
 81    kind: K,
 82    children: SmallVec<[AnyElement<V>; 2]>,
 83    group: Option<SharedString>,
 84    base_style: StyleRefinement,
 85    hover_style: StyleRefinement,
 86    group_hover: Option<GroupStyle>,
 87    active_style: StyleRefinement,
 88    group_active: Option<GroupStyle>,
 89    listeners: MouseEventListeners<V>,
 90}
 91
 92struct GroupStyle {
 93    group: SharedString,
 94    style: StyleRefinement,
 95}
 96
 97impl<V> Div<V, AnonymousElement>
 98where
 99    V: 'static + Send + Sync,
100{
101    pub fn id(self, id: impl Into<ElementId>) -> Div<V, IdentifiedElement> {
102        Div {
103            kind: IdentifiedElement(id.into()),
104            children: self.children,
105            group: self.group,
106            base_style: self.base_style,
107            hover_style: self.hover_style,
108            group_hover: self.group_hover,
109            active_style: self.active_style,
110            group_active: self.group_active,
111            listeners: self.listeners,
112        }
113    }
114}
115
116impl<V, K> Div<V, K>
117where
118    V: 'static + Send + Sync,
119    K: ElementIdentity,
120{
121    pub fn group(mut self, group: impl Into<SharedString>) -> Self {
122        self.group = Some(group.into());
123        self
124    }
125
126    pub fn z_index(mut self, z_index: u32) -> Self {
127        self.base_style.z_index = Some(z_index);
128        self
129    }
130
131    pub fn overflow_hidden(mut self) -> Self {
132        self.base_style.overflow.x = Some(Overflow::Hidden);
133        self.base_style.overflow.y = Some(Overflow::Hidden);
134        self
135    }
136
137    pub fn overflow_hidden_x(mut self) -> Self {
138        self.base_style.overflow.x = Some(Overflow::Hidden);
139        self
140    }
141
142    pub fn overflow_hidden_y(mut self) -> Self {
143        self.base_style.overflow.y = Some(Overflow::Hidden);
144        self
145    }
146
147    pub fn overflow_scroll(mut self, _scroll_state: ScrollState) -> Self {
148        // todo!("impl scrolling")
149        // self.scroll_state = Some(scroll_state);
150        self.base_style.overflow.x = Some(Overflow::Scroll);
151        self.base_style.overflow.y = Some(Overflow::Scroll);
152        self
153    }
154
155    pub fn overflow_x_scroll(mut self, _scroll_state: ScrollState) -> Self {
156        // todo!("impl scrolling")
157        // self.scroll_state = Some(scroll_state);
158        self.base_style.overflow.x = Some(Overflow::Scroll);
159        self
160    }
161
162    pub fn overflow_y_scroll(mut self, _scroll_state: ScrollState) -> Self {
163        // todo!("impl scrolling")
164        // self.scroll_state = Some(scroll_state);
165        self.base_style.overflow.y = Some(Overflow::Scroll);
166        self
167    }
168
169    fn with_element_id<R>(
170        &mut self,
171        cx: &mut ViewContext<V>,
172        f: impl FnOnce(&mut Self, &mut ViewContext<V>) -> R,
173    ) -> R {
174        if let Some(id) = self.id() {
175            cx.with_element_id(id, |cx| f(self, cx))
176        } else {
177            f(self, cx)
178        }
179    }
180
181    pub fn compute_style(
182        &self,
183        bounds: Bounds<Pixels>,
184        state: &DivState,
185        cx: &mut ViewContext<V>,
186    ) -> Style {
187        let mut computed_style = Style::default();
188        computed_style.refine(&self.base_style);
189
190        let mouse_position = cx.mouse_position();
191
192        if let Some(group_hover) = self.group_hover.as_ref() {
193            if let Some(group_bounds) = group_bounds(&group_hover.group, cx) {
194                if group_bounds.contains_point(&mouse_position) {
195                    computed_style.refine(&group_hover.style);
196                }
197            }
198        }
199        if bounds.contains_point(&mouse_position) {
200            computed_style.refine(&self.hover_style);
201        }
202
203        let active_state = *state.active_state.lock();
204        if active_state.group {
205            if let Some(GroupStyle { style, .. }) = self.group_active.as_ref() {
206                computed_style.refine(style);
207            }
208        }
209
210        if active_state.element {
211            computed_style.refine(&self.active_style);
212        }
213
214        computed_style
215    }
216
217    fn paint_hover_listeners(
218        &self,
219        bounds: Bounds<Pixels>,
220        group_bounds: Option<Bounds<Pixels>>,
221        cx: &mut ViewContext<V>,
222    ) {
223        if let Some(group_bounds) = group_bounds {
224            paint_hover_listener(group_bounds, cx);
225        }
226
227        if self.hover_style.is_some() {
228            paint_hover_listener(bounds, cx);
229        }
230    }
231
232    fn paint_active_listener(
233        &self,
234        bounds: Bounds<Pixels>,
235        group_bounds: Option<Bounds<Pixels>>,
236        active_state: Arc<Mutex<ActiveState>>,
237        cx: &mut ViewContext<V>,
238    ) {
239        if active_state.lock().is_none() {
240            cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
241                if phase == DispatchPhase::Bubble {
242                    let group =
243                        group_bounds.map_or(false, |bounds| bounds.contains_point(&down.position));
244                    let element = bounds.contains_point(&down.position);
245                    if group || element {
246                        *active_state.lock() = ActiveState { group, element };
247                        cx.notify();
248                    }
249                }
250            });
251        } else {
252            cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
253                if phase == DispatchPhase::Capture {
254                    *active_state.lock() = ActiveState::default();
255                    cx.notify();
256                }
257            });
258        }
259    }
260
261    fn paint_event_listeners(
262        &self,
263        bounds: Bounds<Pixels>,
264        pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
265        cx: &mut ViewContext<V>,
266    ) {
267        let click_listeners = self.listeners.mouse_click.clone();
268        let mouse_down = pending_click.lock().clone();
269        if let Some(mouse_down) = mouse_down {
270            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
271                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
272                    let mouse_click = MouseClickEvent {
273                        down: mouse_down.clone(),
274                        up: event.clone(),
275                    };
276                    for listener in &click_listeners {
277                        listener(state, &mouse_click, cx);
278                    }
279                }
280
281                *pending_click.lock() = None;
282            });
283        } else {
284            cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
285                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
286                    *pending_click.lock() = Some(event.clone());
287                }
288            });
289        }
290
291        for listener in self.listeners.mouse_down.iter().cloned() {
292            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
293                listener(state, event, &bounds, phase, cx);
294            })
295        }
296
297        for listener in self.listeners.mouse_up.iter().cloned() {
298            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
299                listener(state, event, &bounds, phase, cx);
300            })
301        }
302
303        for listener in self.listeners.mouse_move.iter().cloned() {
304            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
305                listener(state, event, &bounds, phase, cx);
306            })
307        }
308
309        for listener in self.listeners.scroll_wheel.iter().cloned() {
310            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
311                listener(state, event, &bounds, phase, cx);
312            })
313        }
314    }
315}
316
317impl<V, K> Element for Div<V, K>
318where
319    V: 'static + Send + Sync,
320    K: ElementIdentity,
321{
322    type ViewState = V;
323    type ElementState = DivState;
324
325    fn id(&self) -> Option<ElementId> {
326        self.kind.id()
327    }
328
329    fn layout(
330        &mut self,
331        view_state: &mut Self::ViewState,
332        element_state: Option<Self::ElementState>,
333        cx: &mut ViewContext<Self::ViewState>,
334    ) -> (LayoutId, Self::ElementState) {
335        self.with_element_id(cx, |this, cx| {
336            let layout_ids = this
337                .children
338                .iter_mut()
339                .map(|child| child.layout(view_state, cx))
340                .collect::<Vec<_>>();
341
342            let element_state = element_state.unwrap_or_default();
343            let style = this.compute_style(Bounds::default(), &element_state, cx);
344            let layout_id = cx.request_layout(&style, layout_ids);
345            (layout_id, element_state)
346        })
347    }
348
349    fn paint(
350        &mut self,
351        bounds: Bounds<Pixels>,
352        view_state: &mut Self::ViewState,
353        element_state: &mut Self::ElementState,
354        cx: &mut ViewContext<Self::ViewState>,
355    ) {
356        self.with_element_id(cx, |this, cx| {
357            if let Some(group) = this.group.clone() {
358                cx.default_global::<GroupBounds>()
359                    .0
360                    .entry(group)
361                    .or_default()
362                    .push(bounds);
363            }
364
365            let hover_group_bounds = this
366                .group_hover
367                .as_ref()
368                .and_then(|group_hover| group_bounds(&group_hover.group, cx));
369            let active_group_bounds = this
370                .group_active
371                .as_ref()
372                .and_then(|group_active| group_bounds(&group_active.group, cx));
373            let style = this.compute_style(bounds, element_state, cx);
374            let z_index = style.z_index.unwrap_or(0);
375
376            // Paint background and event handlers.
377            cx.stack(z_index, |cx| {
378                cx.stack(0, |cx| {
379                    style.paint(bounds, cx);
380                    this.paint_hover_listeners(bounds, hover_group_bounds, cx);
381                    this.paint_active_listener(
382                        bounds,
383                        active_group_bounds,
384                        element_state.active_state.clone(),
385                        cx,
386                    );
387                    this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx);
388                });
389
390                cx.stack(1, |cx| {
391                    style.apply_text_style(cx, |cx| {
392                        style.apply_overflow(bounds, cx, |cx| {
393                            for child in &mut this.children {
394                                child.paint(view_state, None, cx);
395                            }
396                        })
397                    })
398                });
399            });
400
401            if let Some(group) = this.group.as_ref() {
402                cx.default_global::<GroupBounds>()
403                    .0
404                    .get_mut(group)
405                    .unwrap()
406                    .pop();
407            }
408        })
409    }
410}
411
412impl<V, K> IntoAnyElement<V> for Div<V, K>
413where
414    V: 'static + Send + Sync,
415    K: ElementIdentity,
416{
417    fn into_any(self) -> AnyElement<V> {
418        AnyElement::new(self)
419    }
420}
421
422impl<V, K> ParentElement for Div<V, K>
423where
424    V: 'static + Send + Sync,
425    K: ElementIdentity,
426{
427    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
428        &mut self.children
429    }
430}
431
432impl<V, K> Styled for Div<V, K>
433where
434    V: 'static + Send + Sync,
435    K: ElementIdentity,
436{
437    fn style(&mut self) -> &mut StyleRefinement {
438        &mut self.base_style
439    }
440}
441
442impl<V, K> Interactive for Div<V, K>
443where
444    V: 'static + Send + Sync,
445    K: ElementIdentity,
446{
447    fn listeners(&mut self) -> &mut MouseEventListeners<V> {
448        &mut self.listeners
449    }
450}
451
452impl<V, K> Hover for Div<V, K>
453where
454    V: 'static + Send + Sync,
455    K: ElementIdentity,
456{
457    fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
458        if let Some(group) = group {
459            self.group_hover = Some(GroupStyle { group, style });
460        } else {
461            self.hover_style = style;
462        }
463    }
464}
465
466impl<V> Click for Div<V, IdentifiedElement> where V: 'static + Send + Sync {}
467
468impl<V> Active for Div<V, IdentifiedElement>
469where
470    V: 'static + Send + Sync,
471{
472    fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
473        if let Some(group) = group {
474            self.group_active = Some(GroupStyle { group, style });
475        } else {
476            self.active_style = style;
477        }
478    }
479}
480
481fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
482where
483    V: 'static + Send + Sync,
484{
485    let hovered = bounds.contains_point(&cx.mouse_position());
486    cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
487        if phase == DispatchPhase::Capture {
488            if bounds.contains_point(&event.position) != hovered {
489                cx.notify();
490            }
491        }
492    });
493}