div.rs

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