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