div.rs

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