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 initialize(
373 &mut self,
374 view_state: &mut Self::ViewState,
375 element_state: Option<Self::ElementState>,
376 cx: &mut ViewContext<Self::ViewState>,
377 ) -> Self::ElementState {
378 cx.with_focus(
379 self.focusability.focus_handle().cloned(),
380 self.listeners.key_down.clone(),
381 self.listeners.key_up.clone(),
382 |cx| {
383 for child in &mut self.children {
384 child.initialize(view_state, cx);
385 }
386 element_state.unwrap_or_default()
387 },
388 )
389 }
390
391 fn layout(
392 &mut self,
393 view_state: &mut Self::ViewState,
394 element_state: &mut Self::ElementState,
395 cx: &mut ViewContext<Self::ViewState>,
396 ) -> LayoutId {
397 let style = self.compute_style(Bounds::default(), element_state, cx);
398 style.apply_text_style(cx, |cx| {
399 self.with_element_id(cx, |this, cx| {
400 let layout_ids = this
401 .children
402 .iter_mut()
403 .map(|child| child.layout(view_state, cx))
404 .collect::<Vec<_>>();
405 cx.request_layout(&style, layout_ids)
406 })
407 })
408 }
409
410 fn paint(
411 &mut self,
412 bounds: Bounds<Pixels>,
413 view_state: &mut Self::ViewState,
414 element_state: &mut Self::ElementState,
415 cx: &mut ViewContext<Self::ViewState>,
416 ) {
417 self.with_element_id(cx, |this, cx| {
418 if let Some(group) = this.group.clone() {
419 cx.default_global::<GroupBounds>()
420 .0
421 .entry(group)
422 .or_default()
423 .push(bounds);
424 }
425
426 let hover_group_bounds = this
427 .group_hover
428 .as_ref()
429 .and_then(|group_hover| group_bounds(&group_hover.group, cx));
430 let active_group_bounds = this
431 .group_active
432 .as_ref()
433 .and_then(|group_active| group_bounds(&group_active.group, cx));
434 let style = this.compute_style(bounds, element_state, cx);
435 let z_index = style.z_index.unwrap_or(0);
436
437 // Paint background and event handlers.
438 cx.stack(z_index, |cx| {
439 cx.stack(0, |cx| {
440 style.paint(bounds, cx);
441 this.paint_hover_listeners(bounds, hover_group_bounds, cx);
442 this.paint_active_listener(
443 bounds,
444 active_group_bounds,
445 element_state.active_state.clone(),
446 cx,
447 );
448 this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx);
449 });
450
451 cx.stack(1, |cx| {
452 style.apply_text_style(cx, |cx| {
453 style.apply_overflow(bounds, cx, |cx| {
454 for child in &mut this.children {
455 child.paint(view_state, None, cx);
456 }
457 })
458 })
459 });
460 });
461
462 if let Some(group) = this.group.as_ref() {
463 cx.default_global::<GroupBounds>()
464 .0
465 .get_mut(group)
466 .unwrap()
467 .pop();
468 }
469 })
470 }
471}
472
473impl<I, F, V> IntoAnyElement<V> for Div<I, F, V>
474where
475 I: ElementIdentity,
476 F: ElementFocusability,
477 V: 'static + Send + Sync,
478{
479 fn into_any(self) -> AnyElement<V> {
480 AnyElement::new(self)
481 }
482}
483
484impl<I, F, V> ParentElement for Div<I, F, V>
485where
486 I: ElementIdentity,
487 F: ElementFocusability,
488 V: 'static + Send + Sync,
489{
490 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
491 &mut self.children
492 }
493}
494
495impl<I, F, V> Styled for Div<I, F, V>
496where
497 I: ElementIdentity,
498 F: ElementFocusability,
499 V: 'static + Send + Sync,
500{
501 fn style(&mut self) -> &mut StyleRefinement {
502 &mut self.base_style
503 }
504}
505
506impl<I, F, V> Interactive for Div<I, F, V>
507where
508 I: ElementIdentity,
509 F: ElementFocusability,
510 V: 'static + Send + Sync,
511{
512 fn listeners(&mut self) -> &mut EventListeners<V> {
513 &mut self.listeners
514 }
515}
516
517impl<I, F, V> Hover for Div<I, F, V>
518where
519 I: ElementIdentity,
520 F: ElementFocusability,
521 V: 'static + Send + Sync,
522{
523 fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
524 if let Some(group) = group {
525 self.group_hover = Some(GroupStyle { group, style });
526 } else {
527 self.hover_style = style;
528 }
529 }
530}
531
532impl<F, V> Click for Div<Identified, F, V>
533where
534 F: ElementFocusability,
535 V: 'static + Send + Sync,
536{
537}
538
539impl<F, V> Active for Div<Identified, F, V>
540where
541 F: ElementFocusability,
542 V: 'static + Send + Sync,
543{
544 fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
545 if let Some(group) = group {
546 self.group_active = Some(GroupStyle { group, style });
547 } else {
548 self.active_style = style;
549 }
550 }
551}
552
553fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
554where
555 V: 'static + Send + Sync,
556{
557 let hovered = bounds.contains_point(&cx.mouse_position());
558 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
559 if phase == DispatchPhase::Capture {
560 if bounds.contains_point(&event.position) != hovered {
561 cx.notify();
562 }
563 }
564 });
565}