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        if active_state.element {
210            computed_style.refine(&self.active_style);
211        }
212
213        computed_style
214    }
215
216    fn paint_hover_listeners(
217        &self,
218        bounds: Bounds<Pixels>,
219        group_bounds: Option<Bounds<Pixels>>,
220        cx: &mut ViewContext<V>,
221    ) {
222        if let Some(group_bounds) = group_bounds {
223            paint_hover_listener(group_bounds, cx);
224        }
225
226        if self.hover_style.is_some() {
227            paint_hover_listener(bounds, cx);
228        }
229    }
230
231    fn paint_active_listener(
232        &self,
233        bounds: Bounds<Pixels>,
234        group_bounds: Option<Bounds<Pixels>>,
235        active_state: Arc<Mutex<ActiveState>>,
236        cx: &mut ViewContext<V>,
237    ) {
238        if active_state.lock().is_none() {
239            cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
240                if phase == DispatchPhase::Bubble {
241                    let group =
242                        group_bounds.map_or(false, |bounds| bounds.contains_point(&down.position));
243                    let element = bounds.contains_point(&down.position);
244                    if group || element {
245                        *active_state.lock() = ActiveState { group, element };
246                        cx.notify();
247                    }
248                }
249            });
250        } else {
251            cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
252                if phase == DispatchPhase::Capture {
253                    *active_state.lock() = ActiveState::default();
254                    cx.notify();
255                }
256            });
257        }
258    }
259
260    fn paint_event_listeners(
261        &self,
262        bounds: Bounds<Pixels>,
263        pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
264        cx: &mut ViewContext<V>,
265    ) {
266        let click_listeners = self.listeners.mouse_click.clone();
267        let mouse_down = pending_click.lock().clone();
268        if let Some(mouse_down) = mouse_down {
269            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
270                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
271                    let mouse_click = MouseClickEvent {
272                        down: mouse_down.clone(),
273                        up: event.clone(),
274                    };
275                    for listener in &click_listeners {
276                        listener(state, &mouse_click, cx);
277                    }
278                }
279
280                *pending_click.lock() = None;
281            });
282        } else {
283            cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
284                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
285                    *pending_click.lock() = Some(event.clone());
286                }
287            });
288        }
289
290        for listener in self.listeners.mouse_down.iter().cloned() {
291            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
292                listener(state, event, &bounds, phase, cx);
293            })
294        }
295
296        for listener in self.listeners.mouse_up.iter().cloned() {
297            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
298                listener(state, event, &bounds, phase, cx);
299            })
300        }
301
302        for listener in self.listeners.mouse_move.iter().cloned() {
303            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
304                listener(state, event, &bounds, phase, cx);
305            })
306        }
307
308        for listener in self.listeners.scroll_wheel.iter().cloned() {
309            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
310                listener(state, event, &bounds, phase, cx);
311            })
312        }
313    }
314}
315
316impl<V, K> Element for Div<V, K>
317where
318    V: 'static + Send + Sync,
319    K: ElementIdentity,
320{
321    type ViewState = V;
322    type ElementState = DivState;
323
324    fn id(&self) -> Option<ElementId> {
325        self.kind.id()
326    }
327
328    fn layout(
329        &mut self,
330        view_state: &mut Self::ViewState,
331        element_state: Option<Self::ElementState>,
332        cx: &mut ViewContext<Self::ViewState>,
333    ) -> (LayoutId, Self::ElementState) {
334        let element_state = element_state.unwrap_or_default();
335        let style = self.compute_style(Bounds::default(), &element_state, cx);
336        style.apply_text_style(cx, |cx| {
337            self.with_element_id(cx, |this, cx| {
338                let layout_ids = this
339                    .children
340                    .iter_mut()
341                    .map(|child| child.layout(view_state, cx))
342                    .collect::<Vec<_>>();
343
344                let layout_id = cx.request_layout(&style, layout_ids);
345                (layout_id, element_state)
346            })
347        })
348    }
349
350    fn paint(
351        &mut self,
352        bounds: Bounds<Pixels>,
353        view_state: &mut Self::ViewState,
354        element_state: &mut Self::ElementState,
355        cx: &mut ViewContext<Self::ViewState>,
356    ) {
357        self.with_element_id(cx, |this, cx| {
358            if let Some(group) = this.group.clone() {
359                cx.default_global::<GroupBounds>()
360                    .0
361                    .entry(group)
362                    .or_default()
363                    .push(bounds);
364            }
365
366            let hover_group_bounds = this
367                .group_hover
368                .as_ref()
369                .and_then(|group_hover| group_bounds(&group_hover.group, cx));
370            let active_group_bounds = this
371                .group_active
372                .as_ref()
373                .and_then(|group_active| group_bounds(&group_active.group, cx));
374            let style = this.compute_style(bounds, element_state, cx);
375            let z_index = style.z_index.unwrap_or(0);
376
377            // Paint background and event handlers.
378            cx.stack(z_index, |cx| {
379                cx.stack(0, |cx| {
380                    style.paint(bounds, cx);
381                    this.paint_hover_listeners(bounds, hover_group_bounds, cx);
382                    this.paint_active_listener(
383                        bounds,
384                        active_group_bounds,
385                        element_state.active_state.clone(),
386                        cx,
387                    );
388                    this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx);
389                });
390
391                cx.stack(1, |cx| {
392                    style.apply_text_style(cx, |cx| {
393                        style.apply_overflow(bounds, cx, |cx| {
394                            for child in &mut this.children {
395                                child.paint(view_state, None, cx);
396                            }
397                        })
398                    })
399                });
400            });
401
402            if let Some(group) = this.group.as_ref() {
403                cx.default_global::<GroupBounds>()
404                    .0
405                    .get_mut(group)
406                    .unwrap()
407                    .pop();
408            }
409        })
410    }
411}
412
413impl<V, K> IntoAnyElement<V> for Div<V, K>
414where
415    V: 'static + Send + Sync,
416    K: ElementIdentity,
417{
418    fn into_any(self) -> AnyElement<V> {
419        AnyElement::new(self)
420    }
421}
422
423impl<V, K> ParentElement for Div<V, K>
424where
425    V: 'static + Send + Sync,
426    K: ElementIdentity,
427{
428    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
429        &mut self.children
430    }
431}
432
433impl<V, K> Styled for Div<V, K>
434where
435    V: 'static + Send + Sync,
436    K: ElementIdentity,
437{
438    fn style(&mut self) -> &mut StyleRefinement {
439        &mut self.base_style
440    }
441}
442
443impl<V, K> Interactive for Div<V, K>
444where
445    V: 'static + Send + Sync,
446    K: ElementIdentity,
447{
448    fn listeners(&mut self) -> &mut MouseEventListeners<V> {
449        &mut self.listeners
450    }
451}
452
453impl<V, K> Hover for Div<V, K>
454where
455    V: 'static + Send + Sync,
456    K: ElementIdentity,
457{
458    fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
459        if let Some(group) = group {
460            self.group_hover = Some(GroupStyle { group, style });
461        } else {
462            self.hover_style = style;
463        }
464    }
465}
466
467impl<V> Click for Div<V, IdentifiedElement> where V: 'static + Send + Sync {}
468
469impl<V> Active for Div<V, IdentifiedElement>
470where
471    V: 'static + Send + Sync,
472{
473    fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
474        if let Some(group) = group {
475            self.group_active = Some(GroupStyle { group, style });
476        } else {
477            self.active_style = style;
478        }
479    }
480}
481
482fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
483where
484    V: 'static + Send + Sync,
485{
486    let hovered = bounds.contains_point(&cx.mouse_position());
487    cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
488        if phase == DispatchPhase::Capture {
489            if bounds.contains_point(&event.position) != hovered {
490                cx.notify();
491            }
492        }
493    });
494}