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}