ori_core/views/
div.rs

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}