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}