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, Interactivity, IntoAnyElement, KeyDownEvent,
5 KeyMatch, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow,
6 ParentElement, Pixels, Point, SharedString, Style, StyleRefinement, Styled, ViewContext,
7};
8use collections::HashMap;
9use parking_lot::Mutex;
10use refineable::Refineable;
11use smallvec::SmallVec;
12use std::{any::TypeId, mem, 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 struct Div<
64 V: 'static + Send + Sync,
65 I: ElementIdentity = Anonymous,
66 F: ElementFocusability<V> = NonFocusable,
67> {
68 identity: I,
69 focusability: F,
70 interactivity: Interactivity<V>,
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}
79
80pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
81where
82 V: 'static + Send + Sync,
83{
84 Div {
85 identity: Anonymous,
86 focusability: NonFocusable,
87 interactivity: Interactivity::default(),
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 }
96}
97
98struct GroupStyle {
99 group: SharedString,
100 style: StyleRefinement,
101}
102
103impl<V, F> Div<V, Anonymous, F>
104where
105 F: ElementFocusability<V>,
106 V: 'static + Send + Sync,
107{
108 pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified, F> {
109 Div {
110 identity: Identified(id.into()),
111 focusability: self.focusability,
112 interactivity: self.interactivity,
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 }
121 }
122}
123
124impl<V, I, F> Div<V, I, F>
125where
126 I: ElementIdentity,
127 F: ElementFocusability<V>,
128 V: 'static + Send + Sync,
129{
130 pub fn group(mut self, group: impl Into<SharedString>) -> Self {
131 self.group = Some(group.into());
132 self
133 }
134
135 pub fn z_index(mut self, z_index: u32) -> Self {
136 self.base_style.z_index = Some(z_index);
137 self
138 }
139
140 pub fn overflow_hidden(mut self) -> Self {
141 self.base_style.overflow.x = Some(Overflow::Hidden);
142 self.base_style.overflow.y = Some(Overflow::Hidden);
143 self
144 }
145
146 pub fn overflow_hidden_x(mut self) -> Self {
147 self.base_style.overflow.x = Some(Overflow::Hidden);
148 self
149 }
150
151 pub fn overflow_hidden_y(mut self) -> Self {
152 self.base_style.overflow.y = Some(Overflow::Hidden);
153 self
154 }
155
156 pub fn overflow_scroll(mut self, _scroll_state: ScrollState) -> Self {
157 // todo!("impl scrolling")
158 // self.scroll_state = Some(scroll_state);
159 self.base_style.overflow.x = Some(Overflow::Scroll);
160 self.base_style.overflow.y = Some(Overflow::Scroll);
161 self
162 }
163
164 pub fn overflow_x_scroll(mut self, _scroll_state: ScrollState) -> Self {
165 // todo!("impl scrolling")
166 // self.scroll_state = Some(scroll_state);
167 self.base_style.overflow.x = Some(Overflow::Scroll);
168 self
169 }
170
171 pub fn overflow_y_scroll(mut self, _scroll_state: ScrollState) -> Self {
172 // todo!("impl scrolling")
173 // self.scroll_state = Some(scroll_state);
174 self.base_style.overflow.y = Some(Overflow::Scroll);
175 self
176 }
177
178 fn with_element_id<R>(
179 &mut self,
180 cx: &mut ViewContext<V>,
181 f: impl FnOnce(&mut Self, Option<GlobalElementId>, &mut ViewContext<V>) -> R,
182 ) -> R {
183 if let Some(id) = self.id() {
184 cx.with_element_id(id, |global_id, cx| f(self, Some(global_id), cx))
185 } else {
186 f(self, None, cx)
187 }
188 }
189
190 pub fn compute_style(
191 &self,
192 bounds: Bounds<Pixels>,
193 state: &DivState,
194 cx: &mut ViewContext<V>,
195 ) -> Style {
196 let mut computed_style = Style::default();
197 computed_style.refine(&self.base_style);
198
199 self.focusability.refine_style(&mut computed_style, cx);
200
201 let mouse_position = cx.mouse_position();
202
203 if let Some(group_hover) = self.group_hover.as_ref() {
204 if let Some(group_bounds) = group_bounds(&group_hover.group, cx) {
205 if group_bounds.contains_point(&mouse_position) {
206 computed_style.refine(&group_hover.style);
207 }
208 }
209 }
210 if bounds.contains_point(&mouse_position) {
211 computed_style.refine(&self.hover_style);
212 }
213
214 let active_state = *state.active_state.lock();
215 if active_state.group {
216 if let Some(GroupStyle { style, .. }) = self.group_active.as_ref() {
217 computed_style.refine(style);
218 }
219 }
220 if active_state.element {
221 computed_style.refine(&self.active_style);
222 }
223
224 computed_style
225 }
226
227 fn paint_hover_listeners(
228 &self,
229 bounds: Bounds<Pixels>,
230 group_bounds: Option<Bounds<Pixels>>,
231 cx: &mut ViewContext<V>,
232 ) {
233 if let Some(group_bounds) = group_bounds {
234 paint_hover_listener(group_bounds, cx);
235 }
236
237 if self.hover_style.is_some() {
238 paint_hover_listener(bounds, cx);
239 }
240 }
241
242 fn paint_active_listener(
243 &self,
244 bounds: Bounds<Pixels>,
245 group_bounds: Option<Bounds<Pixels>>,
246 active_state: Arc<Mutex<ActiveState>>,
247 cx: &mut ViewContext<V>,
248 ) {
249 if active_state.lock().is_none() {
250 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
251 if phase == DispatchPhase::Bubble {
252 let group =
253 group_bounds.map_or(false, |bounds| bounds.contains_point(&down.position));
254 let element = bounds.contains_point(&down.position);
255 if group || element {
256 *active_state.lock() = ActiveState { group, element };
257 cx.notify();
258 }
259 }
260 });
261 } else {
262 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
263 if phase == DispatchPhase::Capture {
264 *active_state.lock() = ActiveState::default();
265 cx.notify();
266 }
267 });
268 }
269 }
270}
271
272impl<V, I> Div<V, I, NonFocusable>
273where
274 I: ElementIdentity,
275 V: 'static + Send + Sync,
276{
277 pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable<V>> {
278 Div {
279 identity: self.identity,
280 focusability: handle.clone().into(),
281 children: self.children,
282 group: self.group,
283 base_style: self.base_style,
284 hover_style: self.hover_style,
285 group_hover: self.group_hover,
286 active_style: self.active_style,
287 group_active: self.group_active,
288 interactivity: self.interactivity,
289 }
290 }
291}
292
293impl<V, I> Focus for Div<V, I, Focusable<V>>
294where
295 I: ElementIdentity,
296 V: 'static + Send + Sync,
297{
298 fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
299 &mut self.focusability.focus_listeners
300 }
301
302 fn handle(&self) -> &FocusHandle {
303 &self.focusability.focus_handle
304 }
305
306 fn set_focus_style(&mut self, style: StyleRefinement) {
307 self.focusability.focus_style = style;
308 }
309
310 fn set_focus_in_style(&mut self, style: StyleRefinement) {
311 self.focusability.focus_in_style = style;
312 }
313
314 fn set_in_focus_style(&mut self, style: StyleRefinement) {
315 self.focusability.in_focus_style = style;
316 }
317}
318
319impl<V, I, F> Element for Div<V, I, F>
320where
321 I: ElementIdentity,
322 F: ElementFocusability<V>,
323 V: 'static + Send + Sync,
324{
325 type ViewState = V;
326 type ElementState = DivState;
327
328 fn id(&self) -> Option<ElementId> {
329 self.identity.id()
330 }
331
332 fn initialize(
333 &mut self,
334 view_state: &mut Self::ViewState,
335 element_state: Option<Self::ElementState>,
336 cx: &mut ViewContext<Self::ViewState>,
337 ) -> Self::ElementState {
338 self.with_element_id(cx, |this, global_id, cx| {
339 let element_state = element_state.unwrap_or_default();
340
341 let mut key_listeners = mem::take(&mut this.interactivity.key);
342 if let Some(global_id) = global_id {
343 key_listeners.push((
344 TypeId::of::<KeyDownEvent>(),
345 Arc::new(move |_, key_down, context, phase, cx| {
346 if phase == DispatchPhase::Bubble {
347 let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
348 if let KeyMatch::Some(action) =
349 cx.match_keystroke(&global_id, &key_down.keystroke, context)
350 {
351 return Some(action);
352 }
353 }
354
355 None
356 }),
357 ));
358 }
359
360 cx.with_key_listeners(&key_listeners, |cx| {
361 this.focusability.initialize(cx, |cx| {
362 for child in &mut this.children {
363 child.initialize(view_state, cx);
364 }
365 });
366 });
367 this.interactivity.key = key_listeners;
368
369 element_state
370 })
371 }
372
373 fn layout(
374 &mut self,
375 view_state: &mut Self::ViewState,
376 element_state: &mut Self::ElementState,
377 cx: &mut ViewContext<Self::ViewState>,
378 ) -> LayoutId {
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, _global_id, cx| {
382 let layout_ids = this
383 .children
384 .iter_mut()
385 .map(|child| child.layout(view_state, cx))
386 .collect::<Vec<_>>();
387 cx.request_layout(&style, layout_ids)
388 })
389 })
390 }
391
392 fn paint(
393 &mut self,
394 bounds: Bounds<Pixels>,
395 view_state: &mut Self::ViewState,
396 element_state: &mut Self::ElementState,
397 cx: &mut ViewContext<Self::ViewState>,
398 ) {
399 self.with_element_id(cx, |this, _global_id, cx| {
400 if let Some(group) = this.group.clone() {
401 cx.default_global::<GroupBounds>()
402 .0
403 .entry(group)
404 .or_default()
405 .push(bounds);
406 }
407
408 let hover_group_bounds = this
409 .group_hover
410 .as_ref()
411 .and_then(|group_hover| group_bounds(&group_hover.group, cx));
412 let active_group_bounds = this
413 .group_active
414 .as_ref()
415 .and_then(|group_active| group_bounds(&group_active.group, cx));
416 let style = this.compute_style(bounds, element_state, cx);
417 let z_index = style.z_index.unwrap_or(0);
418
419 // Paint background and event handlers.
420 cx.stack(z_index, |cx| {
421 cx.stack(0, |cx| {
422 style.paint(bounds, cx);
423 this.paint_hover_listeners(bounds, hover_group_bounds, cx);
424 this.paint_active_listener(
425 bounds,
426 active_group_bounds,
427 element_state.active_state.clone(),
428 cx,
429 );
430 this.focusability.paint(bounds, cx);
431 this.interactivity
432 .paint(bounds, element_state.pending_click.clone(), cx);
433 });
434
435 cx.stack(1, |cx| {
436 style.apply_text_style(cx, |cx| {
437 style.apply_overflow(bounds, cx, |cx| {
438 for child in &mut this.children {
439 child.paint(view_state, None, cx);
440 }
441 })
442 })
443 });
444 });
445
446 if let Some(group) = this.group.as_ref() {
447 cx.default_global::<GroupBounds>()
448 .0
449 .get_mut(group)
450 .unwrap()
451 .pop();
452 }
453 })
454 }
455}
456
457impl<V, I, F> IntoAnyElement<V> for Div<V, I, F>
458where
459 I: ElementIdentity,
460 F: ElementFocusability<V>,
461 V: 'static + Send + Sync,
462{
463 fn into_any(self) -> AnyElement<V> {
464 AnyElement::new(self)
465 }
466}
467
468impl<V, I, F> ParentElement for Div<V, I, F>
469where
470 I: ElementIdentity,
471 F: ElementFocusability<V>,
472 V: 'static + Send + Sync,
473{
474 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
475 &mut self.children
476 }
477}
478
479impl<V, I, F> Styled for Div<V, I, F>
480where
481 I: ElementIdentity,
482 F: ElementFocusability<V>,
483 V: 'static + Send + Sync,
484{
485 fn style(&mut self) -> &mut StyleRefinement {
486 &mut self.base_style
487 }
488}
489
490impl<V, I, F> Interactive for Div<V, I, F>
491where
492 I: ElementIdentity,
493 F: ElementFocusability<V>,
494 V: 'static + Send + Sync,
495{
496 fn interactivity(&mut self) -> &mut Interactivity<V> {
497 &mut self.interactivity
498 }
499}
500
501impl<V, I, F> Hover for Div<V, I, F>
502where
503 I: ElementIdentity,
504 F: ElementFocusability<V>,
505 V: 'static + Send + Sync,
506{
507 fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
508 if let Some(group) = group {
509 self.group_hover = Some(GroupStyle { group, style });
510 } else {
511 self.hover_style = style;
512 }
513 }
514}
515
516impl<V, F> Click for Div<V, Identified, F>
517where
518 F: ElementFocusability<V>,
519 V: 'static + Send + Sync,
520{
521}
522
523impl<V, F> Active for Div<V, Identified, F>
524where
525 F: ElementFocusability<V>,
526 V: 'static + Send + Sync,
527{
528 fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
529 if let Some(group) = group {
530 self.group_active = Some(GroupStyle { group, style });
531 } else {
532 self.active_style = style;
533 }
534 }
535}
536
537fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
538where
539 V: 'static + Send + Sync,
540{
541 let hovered = bounds.contains_point(&cx.mouse_position());
542 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
543 if phase == DispatchPhase::Capture {
544 if bounds.contains_point(&event.position) != hovered {
545 cx.notify();
546 }
547 }
548 });
549}