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