1use glam::Vec2;
2use ori_macro::Build;
3
4use crate::{
5 Axis, BoxConstraints, Children, Context, DrawContext, Event, EventContext, EventSignal,
6 FlexLayout, LayoutContext, Node, Parent, PointerEvent, Scope, Sendable, Style, View,
7};
8
9#[derive(Default, Build)]
10pub struct Div {
11 #[event]
12 pub on_event: Option<EventSignal<Event>>,
13 #[event]
14 pub on_press: Option<EventSignal<PointerEvent>>,
15 #[event]
16 pub on_release: Option<EventSignal<PointerEvent>>,
17 pub children: Children,
18}
19
20impl Div {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn child(mut self, child: impl View) -> Self {
26 self.add_child(child);
27 self
28 }
29
30 pub fn on_event<'a>(
31 mut self,
32 cx: Scope<'a>,
33 callback: impl FnMut(&Event) + Sendable + 'a,
34 ) -> Self {
35 self.on_event
36 .get_or_insert_with(|| EventSignal::new())
37 .subscribe(cx, callback);
38
39 self
40 }
41
42 pub fn on_press<'a>(
43 mut self,
44 cx: Scope<'a>,
45 callback: impl FnMut(&PointerEvent) + Sendable + 'a,
46 ) -> Self {
47 self.on_press
48 .get_or_insert_with(|| EventSignal::new())
49 .subscribe(cx, callback);
50
51 self
52 }
53
54 pub fn on_release<'a>(
55 mut self,
56 cx: Scope<'a>,
57 callback: impl FnMut(&PointerEvent) + Sendable + 'a,
58 ) -> Self {
59 self.on_release
60 .get_or_insert_with(|| EventSignal::new())
61 .subscribe(cx, callback);
62
63 self
64 }
65
66 fn handle_pointer_event(&self, cx: &mut EventContext, event: &PointerEvent) -> bool {
67 if event.is_press() && cx.hovered() {
68 if let Some(on_press) = &self.on_press {
69 cx.activate();
70 on_press.emit(event.clone());
71 }
72 } else if event.is_release() && cx.state.active {
73 cx.deactivate();
74
75 if let Some(on_release) = &self.on_release {
76 on_release.emit(event.clone());
77 }
78 } else {
79 return false;
80 }
81
82 true
83 }
84}
85
86impl Parent for Div {
87 fn add_child(&mut self, child: impl View) {
88 self.children.push(Node::new(child));
89 }
90}
91
92impl View for Div {
93 type State = ();
94
95 fn build(&self) -> Self::State {}
96
97 fn style(&self) -> Style {
98 Style::new("div")
99 }
100
101 fn event(&self, _state: &mut Self::State, cx: &mut EventContext, event: &Event) {
102 for child in &self.children {
103 child.event(cx, event);
104 }
105
106 if let Some(on_event) = &self.on_event {
107 on_event.emit(event.clone());
108 }
109
110 if event.is_handled() {
111 return;
112 }
113
114 if let Some(pointer_event) = event.get::<PointerEvent>() {
115 if self.handle_pointer_event(cx, pointer_event) {
116 event.handle();
117 }
118 }
119 }
120
121 fn layout(&self, _state: &mut Self::State, cx: &mut LayoutContext, bc: BoxConstraints) -> Vec2 {
122 let bc = cx.style_constraints(bc);
123
124 let axis = cx.style::<Axis>("direction");
125 let padding = cx.style_range("padding", 0.0..bc.max.min_element() / 2.0);
126 let gap = cx.style_range("gap", 0.0..axis.major(bc.max));
127
128 let justify_content = cx.style("justify-content");
129 let align_items = cx.style("align-items");
130
131 let flex = FlexLayout {
132 axis,
133 justify_content,
134 align_items,
135 gap,
136 offset: Vec2::splat(padding),
137 };
138
139 let content_bc = bc.shrink(Vec2::splat(padding * 2.0));
140 let size = self.children.flex_layout(cx, content_bc, flex);
141
142 size + Vec2::splat(padding * 2.0)
143 }
144
145 fn draw(&self, _state: &mut Self::State, cx: &mut DrawContext) {
146 cx.draw_quad();
147
148 cx.draw_layer(|cx| {
149 for child in &self.children {
150 child.draw(cx);
151 }
152 });
153 }
154}