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