Skip to main content

polyhorn_ios/components/
view.rs

1use polyhorn_core::{use_channel, CommandBuffer as _};
2use polyhorn_ios_sys::coregraphics::CGRect;
3use polyhorn_ios_sys::polykit::{PLYCallback, PLYLayoutEvent, PLYView};
4use polyhorn_ui::geometry::Size;
5
6use crate::handles::ViewHandle;
7use crate::prelude::*;
8use crate::raw::{Apply, Builtin, Container, ContainerID, OpaqueContainer};
9use crate::{Key, Platform, Reference};
10
11pub enum Message {
12    PointerDown,
13    PointerCancel,
14    PointerUp,
15    Layout(CGRect),
16}
17
18impl Container for PLYView {
19    fn mount(&mut self, child: &mut OpaqueContainer) {
20        if let Some(view) = child.container().to_view() {
21            self.add_subview(&view)
22        } else if let Some(view_controller) = child.container().to_view_controller() {
23            self.window()
24                .unwrap()
25                .root_view_controller()
26                .present_view_controller(&view_controller, true, None);
27        }
28    }
29
30    fn unmount(&mut self) {
31        self.remove_from_superview();
32    }
33
34    fn to_view(&self) -> Option<PLYView> {
35        Some(self.clone())
36    }
37}
38
39/// Specializes the generic View component with the iOS-specific concrete
40/// view handle.
41pub type View = polyhorn_ui::components::View<Platform, ViewHandle>;
42
43impl Component for View {
44    fn render(&self, manager: &mut Manager) -> Element {
45        let view_ref: Reference<Option<ContainerID>> = use_reference!(manager, None);
46        let style = self.style;
47
48        if let Some(reference) = self.reference.as_ref() {
49            reference.replace(Some(ViewHandle {
50                compositor: manager.compositor().clone(),
51                container_id: view_ref.weak(manager),
52            }));
53        }
54
55        let on_pointer_cancel_ref = use_reference!(manager, self.on_pointer_cancel.clone());
56        on_pointer_cancel_ref.replace(manager, self.on_pointer_cancel.clone());
57        let on_pointer_cancel_ref = on_pointer_cancel_ref.weak(manager);
58
59        let on_pointer_down_ref = use_reference!(manager, self.on_pointer_down.clone());
60        on_pointer_down_ref.replace(manager, self.on_pointer_down.clone());
61        let on_pointer_down_ref = on_pointer_down_ref.weak(manager);
62
63        let on_pointer_up_ref = use_reference!(manager, self.on_pointer_up.clone());
64        on_pointer_up_ref.replace(manager, self.on_pointer_up.clone());
65        let on_pointer_up_ref = on_pointer_up_ref.weak(manager);
66
67        let on_layout_ref = use_reference!(manager, self.on_layout.clone());
68        on_layout_ref.replace(manager, self.on_layout.clone());
69        let on_layout_ref = on_layout_ref.weak(manager);
70
71        let tx = use_channel!(manager, move |mut rx| {
72            async move {
73                while let Some(message) = rx.next().await {
74                    match message {
75                        Message::PointerCancel => {
76                            on_pointer_cancel_ref.apply(|listener| listener.emit(()));
77                        }
78                        Message::PointerDown => {
79                            on_pointer_down_ref.apply(|listener| listener.emit(()));
80                        }
81                        Message::PointerUp => {
82                            on_pointer_up_ref.apply(|listener| listener.emit(()));
83                        }
84                        Message::Layout(frame) => {
85                            on_layout_ref.apply(|listener| {
86                                listener.emit(Size {
87                                    width: frame.size.width as _,
88                                    height: frame.size.height as _,
89                                })
90                            });
91                        }
92                    }
93                }
94            }
95        });
96
97        use_layout_effect!(manager, move |link, buffer| {
98            let id = match view_ref.apply(link, |view| view.to_owned()) {
99                Some(id) => id,
100                None => return,
101            };
102
103            buffer.mutate(&[id], move |containers, _| {
104                let container = &mut containers[0];
105
106                let layout = match container.layout() {
107                    Some(layout) => layout.clone(),
108                    None => return,
109                };
110
111                if let Some(view) = container.downcast_mut::<PLYView>() {
112                    style.apply(view);
113
114                    view.set_layout(move || {
115                        let current = layout.current();
116
117                        CGRect::new(
118                            current.origin.x as _,
119                            current.origin.y as _,
120                            current.size.width as _,
121                            current.size.height as _,
122                        )
123                    });
124
125                    {
126                        let mut tx = tx.clone();
127
128                        view.set_on_pointer_cancel(PLYCallback::new(move |_| {
129                            let _ = tx.try_send(Message::PointerCancel);
130                        }));
131                    }
132
133                    {
134                        let mut tx = tx.clone();
135
136                        view.set_on_pointer_down(PLYCallback::new(move |_| {
137                            let _ = tx.try_send(Message::PointerDown);
138                        }));
139                    }
140
141                    {
142                        let mut tx = tx.clone();
143
144                        view.set_on_pointer_up(PLYCallback::new(move |_| {
145                            let _ = tx.try_send(Message::PointerUp);
146                        }));
147                    }
148
149                    {
150                        let mut tx = tx.clone();
151
152                        view.set_on_layout(PLYCallback::new(move |event: PLYLayoutEvent| {
153                            let _ = tx.try_send(Message::Layout(event.frame()));
154                        }));
155                    }
156                }
157            });
158        });
159
160        Element::builtin(
161            Key::new(()),
162            Builtin::View(self.style),
163            manager.children(),
164            Some(view_ref.weak(manager)),
165        )
166    }
167}