1use crate::{
2 Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
3 ElementFocusability, ElementId, ElementIdentity, Focus, FocusHandle, FocusListeners, Focusable,
4 GlobalElementId, Hover, Identified, Interactive, InteractiveState, IntoAnyElement,
5 KeyDownEvent, KeyMatch, LayoutId, MouseClickEvent, MouseDownEvent, MouseMoveEvent,
6 MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point, ScrollWheelEvent,
7 SharedString, 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: ElementIdentity = Anonymous,
67 F: ElementFocusability<V> = NonFocusable,
68> {
69 identity: I,
70 focusability: F,
71 children: SmallVec<[AnyElement<V>; 2]>,
72 group: Option<SharedString>,
73 base_style: StyleRefinement,
74 hover_style: StyleRefinement,
75 group_hover: Option<GroupStyle>,
76 active_style: StyleRefinement,
77 group_active: Option<GroupStyle>,
78 interactive_state: InteractiveState<V>,
79}
80
81pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
82where
83 V: 'static + Send + Sync,
84{
85 Div {
86 identity: Anonymous,
87 focusability: NonFocusable,
88 children: SmallVec::new(),
89 group: None,
90 base_style: StyleRefinement::default(),
91 hover_style: StyleRefinement::default(),
92 group_hover: None,
93 active_style: StyleRefinement::default(),
94 group_active: None,
95 interactive_state: InteractiveState::default(),
96 }
97}
98
99struct GroupStyle {
100 group: SharedString,
101 style: StyleRefinement,
102}
103
104impl<V, F> Div<V, Anonymous, F>
105where
106 F: ElementFocusability<V>,
107 V: 'static + Send + Sync,
108{
109 pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified, F> {
110 Div {
111 identity: Identified(id.into()),
112 focusability: self.focusability,
113 children: self.children,
114 group: self.group,
115 base_style: self.base_style,
116 hover_style: self.hover_style,
117 group_hover: self.group_hover,
118 active_style: self.active_style,
119 group_active: self.group_active,
120 interactive_state: self.interactive_state,
121 }
122 }
123}
124
125impl<V, I, F> Div<V, I, F>
126where
127 I: ElementIdentity,
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 fn paint_event_listeners(
273 &mut self,
274 bounds: Bounds<Pixels>,
275 pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
276 cx: &mut ViewContext<V>,
277 ) {
278 let click_listeners = mem::take(&mut self.interactive_state.mouse_click);
279
280 let mouse_down = pending_click.lock().clone();
281 if let Some(mouse_down) = mouse_down {
282 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
283 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
284 let mouse_click = MouseClickEvent {
285 down: mouse_down.clone(),
286 up: event.clone(),
287 };
288 for listener in &click_listeners {
289 listener(state, &mouse_click, cx);
290 }
291 }
292
293 *pending_click.lock() = None;
294 });
295 } else {
296 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
297 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
298 *pending_click.lock() = Some(event.clone());
299 }
300 });
301 }
302
303 for listener in mem::take(&mut self.interactive_state.mouse_down) {
304 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
305 listener(state, event, &bounds, phase, cx);
306 })
307 }
308
309 for listener in mem::take(&mut self.interactive_state.mouse_up) {
310 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
311 listener(state, event, &bounds, phase, cx);
312 })
313 }
314
315 for listener in mem::take(&mut self.interactive_state.mouse_move) {
316 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
317 listener(state, event, &bounds, phase, cx);
318 })
319 }
320
321 for listener in mem::take(&mut self.interactive_state.scroll_wheel) {
322 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
323 listener(state, event, &bounds, phase, cx);
324 })
325 }
326 }
327}
328
329impl<V, I> Div<V, I, NonFocusable>
330where
331 I: ElementIdentity,
332 V: 'static + Send + Sync,
333{
334 pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable<V>> {
335 Div {
336 identity: self.identity,
337 focusability: handle.clone().into(),
338 children: self.children,
339 group: self.group,
340 base_style: self.base_style,
341 hover_style: self.hover_style,
342 group_hover: self.group_hover,
343 active_style: self.active_style,
344 group_active: self.group_active,
345 interactive_state: self.interactive_state,
346 }
347 }
348}
349
350impl<V, I> Focus for Div<V, I, Focusable<V>>
351where
352 I: ElementIdentity,
353 V: 'static + Send + Sync,
354{
355 fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
356 &mut self.focusability.focus_listeners
357 }
358
359 fn handle(&self) -> &FocusHandle {
360 &self.focusability.focus_handle
361 }
362
363 fn set_focus_style(&mut self, style: StyleRefinement) {
364 self.focusability.focus_style = style;
365 }
366
367 fn set_focus_in_style(&mut self, style: StyleRefinement) {
368 self.focusability.focus_in_style = style;
369 }
370
371 fn set_in_focus_style(&mut self, style: StyleRefinement) {
372 self.focusability.in_focus_style = style;
373 }
374}
375
376impl<V, I, F> Element for Div<V, I, F>
377where
378 I: ElementIdentity,
379 F: ElementFocusability<V>,
380 V: 'static + Send + Sync,
381{
382 type ViewState = V;
383 type ElementState = DivState;
384
385 fn id(&self) -> Option<ElementId> {
386 self.identity.id()
387 }
388
389 fn initialize(
390 &mut self,
391 view_state: &mut Self::ViewState,
392 element_state: Option<Self::ElementState>,
393 cx: &mut ViewContext<Self::ViewState>,
394 ) -> Self::ElementState {
395 self.with_element_id(cx, |this, global_id, cx| {
396 let element_state = element_state.unwrap_or_default();
397
398 let mut key_listeners = mem::take(&mut this.interactive_state.key);
399 if let Some(global_id) = global_id {
400 key_listeners.push((
401 TypeId::of::<KeyDownEvent>(),
402 Arc::new(move |_, key_down, context, phase, cx| {
403 if phase == DispatchPhase::Bubble {
404 let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
405 if let KeyMatch::Some(action) =
406 cx.match_keystroke(&global_id, &key_down.keystroke, context)
407 {
408 return Some(action);
409 }
410 }
411
412 None
413 }),
414 ));
415 }
416
417 cx.with_key_listeners(&key_listeners, |cx| {
418 this.focusability.initialize(cx, |cx| {
419 for child in &mut this.children {
420 child.initialize(view_state, cx);
421 }
422 });
423 });
424 this.interactive_state.key = key_listeners;
425
426 element_state
427 })
428 }
429
430 fn layout(
431 &mut self,
432 view_state: &mut Self::ViewState,
433 element_state: &mut Self::ElementState,
434 cx: &mut ViewContext<Self::ViewState>,
435 ) -> LayoutId {
436 let style = self.compute_style(Bounds::default(), element_state, cx);
437 style.apply_text_style(cx, |cx| {
438 self.with_element_id(cx, |this, _global_id, cx| {
439 let layout_ids = this
440 .children
441 .iter_mut()
442 .map(|child| child.layout(view_state, cx))
443 .collect::<Vec<_>>();
444 cx.request_layout(&style, layout_ids)
445 })
446 })
447 }
448
449 fn paint(
450 &mut self,
451 bounds: Bounds<Pixels>,
452 view_state: &mut Self::ViewState,
453 element_state: &mut Self::ElementState,
454 cx: &mut ViewContext<Self::ViewState>,
455 ) {
456 self.with_element_id(cx, |this, _global_id, cx| {
457 if let Some(group) = this.group.clone() {
458 cx.default_global::<GroupBounds>()
459 .0
460 .entry(group)
461 .or_default()
462 .push(bounds);
463 }
464
465 let hover_group_bounds = this
466 .group_hover
467 .as_ref()
468 .and_then(|group_hover| group_bounds(&group_hover.group, cx));
469 let active_group_bounds = this
470 .group_active
471 .as_ref()
472 .and_then(|group_active| group_bounds(&group_active.group, cx));
473 let style = this.compute_style(bounds, element_state, cx);
474 let z_index = style.z_index.unwrap_or(0);
475
476 // Paint background and event handlers.
477 cx.stack(z_index, |cx| {
478 cx.stack(0, |cx| {
479 style.paint(bounds, cx);
480 this.paint_hover_listeners(bounds, hover_group_bounds, cx);
481 this.paint_active_listener(
482 bounds,
483 active_group_bounds,
484 element_state.active_state.clone(),
485 cx,
486 );
487 this.focusability.paint(bounds, cx);
488 this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx);
489 });
490
491 cx.stack(1, |cx| {
492 style.apply_text_style(cx, |cx| {
493 style.apply_overflow(bounds, cx, |cx| {
494 for child in &mut this.children {
495 child.paint(view_state, None, cx);
496 }
497 })
498 })
499 });
500 });
501
502 if let Some(group) = this.group.as_ref() {
503 cx.default_global::<GroupBounds>()
504 .0
505 .get_mut(group)
506 .unwrap()
507 .pop();
508 }
509 })
510 }
511}
512
513impl<V, I, F> IntoAnyElement<V> for Div<V, I, F>
514where
515 I: ElementIdentity,
516 F: ElementFocusability<V>,
517 V: 'static + Send + Sync,
518{
519 fn into_any(self) -> AnyElement<V> {
520 AnyElement::new(self)
521 }
522}
523
524impl<V, I, F> ParentElement for Div<V, I, F>
525where
526 I: ElementIdentity,
527 F: ElementFocusability<V>,
528 V: 'static + Send + Sync,
529{
530 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
531 &mut self.children
532 }
533}
534
535impl<V, I, F> Styled for Div<V, I, F>
536where
537 I: ElementIdentity,
538 F: ElementFocusability<V>,
539 V: 'static + Send + Sync,
540{
541 fn style(&mut self) -> &mut StyleRefinement {
542 &mut self.base_style
543 }
544}
545
546impl<V, I, F> Interactive for Div<V, I, F>
547where
548 I: ElementIdentity,
549 F: ElementFocusability<V>,
550 V: 'static + Send + Sync,
551{
552 fn interactive_state(&mut self) -> &mut InteractiveState<V> {
553 &mut self.interactive_state
554 }
555}
556
557impl<V, I, F> Hover for Div<V, I, F>
558where
559 I: ElementIdentity,
560 F: ElementFocusability<V>,
561 V: 'static + Send + Sync,
562{
563 fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
564 if let Some(group) = group {
565 self.group_hover = Some(GroupStyle { group, style });
566 } else {
567 self.hover_style = style;
568 }
569 }
570}
571
572impl<V, F> Click for Div<V, Identified, F>
573where
574 F: ElementFocusability<V>,
575 V: 'static + Send + Sync,
576{
577}
578
579impl<V, F> Active for Div<V, Identified, F>
580where
581 F: ElementFocusability<V>,
582 V: 'static + Send + Sync,
583{
584 fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
585 if let Some(group) = group {
586 self.group_active = Some(GroupStyle { group, style });
587 } else {
588 self.active_style = style;
589 }
590 }
591}
592
593fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
594where
595 V: 'static + Send + Sync,
596{
597 let hovered = bounds.contains_point(&cx.mouse_position());
598 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
599 if phase == DispatchPhase::Capture {
600 if bounds.contains_point(&event.position) != hovered {
601 cx.notify();
602 }
603 }
604 });
605}