div.rs

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