div.rs

  1use crate::{
  2    Active, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
  3    ElementFocusability, ElementId, ElementInteractivity, Focus, FocusHandle, FocusListeners,
  4    Focusable, GlobalElementId, Hover, Interactive, Interactivity, IntoAnyElement, KeyDownEvent,
  5    KeyMatch, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow,
  6    ParentElement, Pixels, Point, SharedString, StatefulInteractivity, StatelessInteractivity,
  7    Style, StyleRefinement, Styled, 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 struct Div<
 65    V: 'static + Send + Sync,
 66    I: ElementInteractivity<V> = StatelessInteractivity<V>,
 67    F: ElementFocusability<V> = NonFocusable,
 68> {
 69    identity: I,
 70    focusability: F,
 71    interactivity: Interactivity<V>,
 72    children: SmallVec<[AnyElement<V>; 2]>,
 73    group: Option<SharedString>,
 74    base_style: StyleRefinement,
 75    hover_style: StyleRefinement,
 76    group_hover: Option<GroupStyle>,
 77    active_style: StyleRefinement,
 78    group_active: Option<GroupStyle>,
 79}
 80
 81pub fn div<V>() -> Div<V, StatelessInteractivity<V>, NonFocusable>
 82where
 83    V: 'static + Send + Sync,
 84{
 85    Div {
 86        identity: StatelessInteractivity::default(),
 87        focusability: NonFocusable,
 88        interactivity: Interactivity::default(),
 89        children: SmallVec::new(),
 90        group: None,
 91        base_style: StyleRefinement::default(),
 92        hover_style: StyleRefinement::default(),
 93        group_hover: None,
 94        active_style: StyleRefinement::default(),
 95        group_active: None,
 96    }
 97}
 98
 99struct GroupStyle {
100    group: SharedString,
101    style: StyleRefinement,
102}
103
104impl<V, F> Div<V, StatelessInteractivity<V>, F>
105where
106    F: ElementFocusability<V>,
107    V: 'static + Send + Sync,
108{
109    pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteractivity<V>, F> {
110        Div {
111            identity: id.into().into(),
112            focusability: self.focusability,
113            interactivity: self.interactivity,
114            children: self.children,
115            group: self.group,
116            base_style: self.base_style,
117            hover_style: self.hover_style,
118            group_hover: self.group_hover,
119            active_style: self.active_style,
120            group_active: self.group_active,
121        }
122    }
123}
124
125impl<V, I, F> Div<V, I, F>
126where
127    I: ElementInteractivity<V>,
128    F: ElementFocusability<V>,
129    V: 'static + Send + Sync,
130{
131    pub fn group(mut self, group: impl Into<SharedString>) -> Self {
132        self.group = Some(group.into());
133        self
134    }
135
136    pub fn z_index(mut self, z_index: u32) -> Self {
137        self.base_style.z_index = Some(z_index);
138        self
139    }
140
141    pub fn overflow_hidden(mut self) -> Self {
142        self.base_style.overflow.x = Some(Overflow::Hidden);
143        self.base_style.overflow.y = Some(Overflow::Hidden);
144        self
145    }
146
147    pub fn overflow_hidden_x(mut self) -> Self {
148        self.base_style.overflow.x = Some(Overflow::Hidden);
149        self
150    }
151
152    pub fn overflow_hidden_y(mut self) -> Self {
153        self.base_style.overflow.y = Some(Overflow::Hidden);
154        self
155    }
156
157    pub fn overflow_scroll(mut self, _scroll_state: ScrollState) -> Self {
158        // todo!("impl scrolling")
159        // self.scroll_state = Some(scroll_state);
160        self.base_style.overflow.x = Some(Overflow::Scroll);
161        self.base_style.overflow.y = Some(Overflow::Scroll);
162        self
163    }
164
165    pub fn overflow_x_scroll(mut self, _scroll_state: ScrollState) -> Self {
166        // todo!("impl scrolling")
167        // self.scroll_state = Some(scroll_state);
168        self.base_style.overflow.x = Some(Overflow::Scroll);
169        self
170    }
171
172    pub fn overflow_y_scroll(mut self, _scroll_state: ScrollState) -> Self {
173        // todo!("impl scrolling")
174        // self.scroll_state = Some(scroll_state);
175        self.base_style.overflow.y = Some(Overflow::Scroll);
176        self
177    }
178
179    fn with_element_id<R>(
180        &mut self,
181        cx: &mut ViewContext<V>,
182        f: impl FnOnce(&mut Self, Option<GlobalElementId>, &mut ViewContext<V>) -> R,
183    ) -> R {
184        if let Some(id) = self.id() {
185            cx.with_element_id(id, |global_id, cx| f(self, Some(global_id), cx))
186        } else {
187            f(self, None, cx)
188        }
189    }
190
191    pub fn compute_style(
192        &self,
193        bounds: Bounds<Pixels>,
194        state: &DivState,
195        cx: &mut ViewContext<V>,
196    ) -> Style {
197        let mut computed_style = Style::default();
198        computed_style.refine(&self.base_style);
199
200        self.focusability.refine_style(&mut computed_style, cx);
201
202        let mouse_position = cx.mouse_position();
203
204        if let Some(group_hover) = self.group_hover.as_ref() {
205            if let Some(group_bounds) = group_bounds(&group_hover.group, cx) {
206                if group_bounds.contains_point(&mouse_position) {
207                    computed_style.refine(&group_hover.style);
208                }
209            }
210        }
211        if bounds.contains_point(&mouse_position) {
212            computed_style.refine(&self.hover_style);
213        }
214
215        let active_state = *state.active_state.lock();
216        if active_state.group {
217            if let Some(GroupStyle { style, .. }) = self.group_active.as_ref() {
218                computed_style.refine(style);
219            }
220        }
221        if active_state.element {
222            computed_style.refine(&self.active_style);
223        }
224
225        computed_style
226    }
227
228    fn paint_hover_listeners(
229        &self,
230        bounds: Bounds<Pixels>,
231        group_bounds: Option<Bounds<Pixels>>,
232        cx: &mut ViewContext<V>,
233    ) {
234        if let Some(group_bounds) = group_bounds {
235            paint_hover_listener(group_bounds, cx);
236        }
237
238        if self.hover_style.is_some() {
239            paint_hover_listener(bounds, cx);
240        }
241    }
242
243    fn paint_active_listener(
244        &self,
245        bounds: Bounds<Pixels>,
246        group_bounds: Option<Bounds<Pixels>>,
247        active_state: Arc<Mutex<ActiveState>>,
248        cx: &mut ViewContext<V>,
249    ) {
250        if active_state.lock().is_none() {
251            cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
252                if phase == DispatchPhase::Bubble {
253                    let group =
254                        group_bounds.map_or(false, |bounds| bounds.contains_point(&down.position));
255                    let element = bounds.contains_point(&down.position);
256                    if group || element {
257                        *active_state.lock() = ActiveState { group, element };
258                        cx.notify();
259                    }
260                }
261            });
262        } else {
263            cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
264                if phase == DispatchPhase::Capture {
265                    *active_state.lock() = ActiveState::default();
266                    cx.notify();
267                }
268            });
269        }
270    }
271}
272
273impl<V, I> Div<V, I, NonFocusable>
274where
275    I: ElementInteractivity<V>,
276    V: 'static + Send + Sync,
277{
278    pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable<V>> {
279        Div {
280            identity: self.identity,
281            focusability: handle.clone().into(),
282            children: self.children,
283            group: self.group,
284            base_style: self.base_style,
285            hover_style: self.hover_style,
286            group_hover: self.group_hover,
287            active_style: self.active_style,
288            group_active: self.group_active,
289            interactivity: self.interactivity,
290        }
291    }
292}
293
294impl<V, I> Focus for Div<V, I, Focusable<V>>
295where
296    I: ElementInteractivity<V>,
297    V: 'static + Send + Sync,
298{
299    fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
300        &mut self.focusability.focus_listeners
301    }
302
303    fn handle(&self) -> &FocusHandle {
304        &self.focusability.focus_handle
305    }
306
307    fn set_focus_style(&mut self, style: StyleRefinement) {
308        self.focusability.focus_style = style;
309    }
310
311    fn set_focus_in_style(&mut self, style: StyleRefinement) {
312        self.focusability.focus_in_style = style;
313    }
314
315    fn set_in_focus_style(&mut self, style: StyleRefinement) {
316        self.focusability.in_focus_style = style;
317    }
318}
319
320impl<V, I, F> Element for Div<V, I, F>
321where
322    I: ElementInteractivity<V>,
323    F: ElementFocusability<V>,
324    V: 'static + Send + Sync,
325{
326    type ViewState = V;
327    type ElementState = DivState;
328
329    fn id(&self) -> Option<ElementId> {
330        self.identity
331            .as_stateful()
332            .map(|identified| identified.id.clone())
333    }
334
335    fn initialize(
336        &mut self,
337        view_state: &mut Self::ViewState,
338        element_state: Option<Self::ElementState>,
339        cx: &mut ViewContext<Self::ViewState>,
340    ) -> Self::ElementState {
341        self.with_element_id(cx, |this, global_id, cx| {
342            let element_state = element_state.unwrap_or_default();
343
344            let mut key_listeners = mem::take(&mut this.interactivity.key);
345            if let Some(global_id) = global_id {
346                key_listeners.push((
347                    TypeId::of::<KeyDownEvent>(),
348                    Arc::new(move |_, key_down, context, phase, cx| {
349                        if phase == DispatchPhase::Bubble {
350                            let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
351                            if let KeyMatch::Some(action) =
352                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
353                            {
354                                return Some(action);
355                            }
356                        }
357
358                        None
359                    }),
360                ));
361            }
362
363            cx.with_key_listeners(&key_listeners, |cx| {
364                this.focusability.initialize(cx, |cx| {
365                    for child in &mut this.children {
366                        child.initialize(view_state, cx);
367                    }
368                });
369            });
370            this.interactivity.key = key_listeners;
371
372            element_state
373        })
374    }
375
376    fn layout(
377        &mut self,
378        view_state: &mut Self::ViewState,
379        element_state: &mut Self::ElementState,
380        cx: &mut ViewContext<Self::ViewState>,
381    ) -> LayoutId {
382        let style = self.compute_style(Bounds::default(), element_state, cx);
383        style.apply_text_style(cx, |cx| {
384            self.with_element_id(cx, |this, _global_id, cx| {
385                let layout_ids = this
386                    .children
387                    .iter_mut()
388                    .map(|child| child.layout(view_state, cx))
389                    .collect::<Vec<_>>();
390                cx.request_layout(&style, layout_ids)
391            })
392        })
393    }
394
395    fn paint(
396        &mut self,
397        bounds: Bounds<Pixels>,
398        view_state: &mut Self::ViewState,
399        element_state: &mut Self::ElementState,
400        cx: &mut ViewContext<Self::ViewState>,
401    ) {
402        self.with_element_id(cx, |this, _global_id, cx| {
403            if let Some(group) = this.group.clone() {
404                cx.default_global::<GroupBounds>()
405                    .0
406                    .entry(group)
407                    .or_default()
408                    .push(bounds);
409            }
410
411            let hover_group_bounds = this
412                .group_hover
413                .as_ref()
414                .and_then(|group_hover| group_bounds(&group_hover.group, cx));
415            let active_group_bounds = this
416                .group_active
417                .as_ref()
418                .and_then(|group_active| group_bounds(&group_active.group, cx));
419            let style = this.compute_style(bounds, element_state, cx);
420            let z_index = style.z_index.unwrap_or(0);
421
422            // Paint background and event handlers.
423            cx.stack(z_index, |cx| {
424                cx.stack(0, |cx| {
425                    style.paint(bounds, cx);
426                    this.paint_hover_listeners(bounds, hover_group_bounds, cx);
427                    this.paint_active_listener(
428                        bounds,
429                        active_group_bounds,
430                        element_state.active_state.clone(),
431                        cx,
432                    );
433                    this.focusability.paint(bounds, cx);
434                    this.interactivity
435                        .paint(bounds, element_state.pending_click.clone(), cx);
436                });
437
438                cx.stack(1, |cx| {
439                    style.apply_text_style(cx, |cx| {
440                        style.apply_overflow(bounds, cx, |cx| {
441                            for child in &mut this.children {
442                                child.paint(view_state, None, cx);
443                            }
444                        })
445                    })
446                });
447            });
448
449            if let Some(group) = this.group.as_ref() {
450                cx.default_global::<GroupBounds>()
451                    .0
452                    .get_mut(group)
453                    .unwrap()
454                    .pop();
455            }
456        })
457    }
458}
459
460impl<V, I, F> IntoAnyElement<V> for Div<V, I, F>
461where
462    I: ElementInteractivity<V>,
463    F: ElementFocusability<V>,
464    V: 'static + Send + Sync,
465{
466    fn into_any(self) -> AnyElement<V> {
467        AnyElement::new(self)
468    }
469}
470
471impl<V, I, F> ParentElement for Div<V, I, F>
472where
473    I: ElementInteractivity<V>,
474    F: ElementFocusability<V>,
475    V: 'static + Send + Sync,
476{
477    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
478        &mut self.children
479    }
480}
481
482impl<V, I, F> Styled for Div<V, I, F>
483where
484    I: ElementInteractivity<V>,
485    F: ElementFocusability<V>,
486    V: 'static + Send + Sync,
487{
488    fn style(&mut self) -> &mut StyleRefinement {
489        &mut self.base_style
490    }
491}
492
493impl<V, I, F> Interactive for Div<V, I, F>
494where
495    I: ElementInteractivity<V>,
496    F: ElementFocusability<V>,
497    V: 'static + Send + Sync,
498{
499    fn interactivity(&mut self) -> &mut Interactivity<V> {
500        &mut self.interactivity
501    }
502}
503
504impl<V, I, F> Hover for Div<V, I, F>
505where
506    I: ElementInteractivity<V>,
507    F: ElementFocusability<V>,
508    V: 'static + Send + Sync,
509{
510    fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
511        if let Some(group) = group {
512            self.group_hover = Some(GroupStyle { group, style });
513        } else {
514            self.hover_style = style;
515        }
516    }
517}
518
519impl<V, F> Click for Div<V, StatefulInteractivity<V>, F>
520where
521    F: ElementFocusability<V>,
522    V: 'static + Send + Sync,
523{
524}
525
526impl<V, F> Active for Div<V, StatefulInteractivity<V>, F>
527where
528    F: ElementFocusability<V>,
529    V: 'static + Send + Sync,
530{
531    fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
532        if let Some(group) = group {
533            self.group_active = Some(GroupStyle { group, style });
534        } else {
535            self.active_style = style;
536        }
537    }
538}
539
540fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
541where
542    V: 'static + Send + Sync,
543{
544    let hovered = bounds.contains_point(&cx.mouse_position());
545    cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
546        if phase == DispatchPhase::Capture {
547            if bounds.contains_point(&event.position) != hovered {
548                cx.notify();
549            }
550        }
551    });
552}