Skip to main content

polyhorn_ios/raw/
builtin.rs

1use polyhorn_ios_sys::polykit::{
2    PLYImageView, PLYKeyboardAvoidingView, PLYLabel, PLYScrollView, PLYTextInputView, PLYView,
3    PLYViewController, PLYWindow,
4};
5use polyhorn_ios_sys::uikit::UIApplication;
6use polyhorn_ui::geometry::{Dimension, Size};
7use polyhorn_ui::layout::{LayoutNode, MeasureFunc};
8use polyhorn_ui::styles::{FlexDirection, Position, Relative, ViewStyle};
9
10use super::{Container, Environment, OpaqueContainer, Platform};
11
12/// Defines one of the native views that bridge Polyhorn with iOS's UIKit.
13#[derive(Clone, Debug)]
14pub enum Builtin {
15    /// Renders an image.
16    ImageView(ViewStyle),
17
18    /// Automatically adjusts its layout when the system keyboard appears,
19    /// changes its dimensions or disappears.
20    KeyboardAvoidingView,
21
22    /// Renders (rich) text.
23    Label(MeasureFunc),
24
25    /// Renders a view in a system-provided modal window.
26    Modal,
27
28    /// Implements scrolling gestures to facilitate layouts that exceed screen
29    /// sizes.
30    ScrollView {
31        /// This is the style that gets applied to the scroll view itself.
32        self_style: ViewStyle,
33
34        /// This is the style that gets applied to the content of the scroll
35        /// view.
36        content_style: ViewStyle,
37    },
38
39    /// Accepts user input.
40    TextInput,
41
42    /// The base component.
43    View(ViewStyle),
44
45    /// The root component.
46    Window,
47}
48
49impl Container for UIApplication {
50    fn mount(&mut self, child: &mut OpaqueContainer) {
51        let container = child.container();
52        if let Some(mut window) = container.to_window() {
53            window.make_key_and_visible()
54        }
55    }
56
57    fn unmount(&mut self) {
58        // Applications are never unmounted.
59        unimplemented!("Applications cannot be unmounted.");
60    }
61}
62
63impl polyhorn_core::Builtin<Platform> for Builtin {
64    fn instantiate(
65        &self,
66        _parent: &mut OpaqueContainer,
67        environment: &mut Environment,
68    ) -> OpaqueContainer {
69        let layout = match self {
70            Builtin::Label(_) => LayoutNode::leaf(environment.layout_tree().clone()),
71            _ => LayoutNode::new(environment.layout_tree().clone()),
72        };
73
74        let mut container = match self {
75            &Builtin::ImageView(_) => OpaqueContainer::new(layout, None, PLYImageView::new()),
76            Builtin::KeyboardAvoidingView => {
77                layout.set_style(ViewStyle {
78                    position: Position::Relative(Relative {
79                        flex_grow: 1.0,
80                        ..Default::default()
81                    }),
82                    ..Default::default()
83                });
84                OpaqueContainer::new(layout, None, PLYKeyboardAvoidingView::new())
85            }
86            Builtin::Label(_) => OpaqueContainer::new(layout, None, PLYLabel::new()),
87            Builtin::Modal => {
88                let view_controller = PLYViewController::new();
89
90                OpaqueContainer::new(layout, None, view_controller)
91            }
92            Builtin::ScrollView { .. } => {
93                let content_layout = LayoutNode::new(environment.layout_tree().clone());
94                OpaqueContainer::new(layout, Some(content_layout), PLYScrollView::new())
95            }
96            Builtin::TextInput => {
97                layout.set_style(ViewStyle {
98                    size: Size {
99                        width: Dimension::Percentage(1.0),
100                        height: Dimension::Auto,
101                    },
102                    ..Default::default()
103                });
104                OpaqueContainer::new(layout, None, PLYTextInputView::new())
105            }
106            Builtin::View(_) => OpaqueContainer::new(layout, None, PLYView::new()),
107            Builtin::Window => {
108                let mut window = PLYWindow::new();
109                window.set_root_view_controller(PLYViewController::new());
110
111                // TODO: where do we remove roots?
112                layout
113                    .layouter()
114                    .write()
115                    .unwrap()
116                    .roots_mut()
117                    .push(layout.node());
118
119                OpaqueContainer::new(layout, None, window)
120            }
121        };
122
123        self.update(&mut container, environment);
124
125        container
126    }
127
128    fn update(&self, container: &mut OpaqueContainer, _environment: &mut Environment) {
129        match self {
130            &Builtin::ImageView(style) => container.layout().unwrap().set_style(style),
131            Builtin::Label(measure) => container.layout().unwrap().set_measure(measure.clone()),
132            Builtin::Modal => {
133                let view_controller =
134                    if let Some(view_controller) = container.downcast_mut::<PLYViewController>() {
135                        view_controller
136                    } else {
137                        return;
138                    };
139
140                let view = view_controller.view_mut();
141                view.set_needs_layout();
142                let frame = view.frame();
143
144                container.layout().unwrap().set_style(ViewStyle {
145                    position: Position::Absolute(Default::default()),
146                    flex_direction: FlexDirection::Column,
147                    size: Size {
148                        width: Dimension::Points(frame.size.width as _),
149                        height: Dimension::Points(frame.size.height as _),
150                    },
151                    ..Default::default()
152                });
153            }
154            &Builtin::ScrollView {
155                self_style,
156                content_style,
157            } => {
158                container.layout().unwrap().set_style(self_style);
159                container.content_layout().unwrap().set_style(content_style);
160            }
161            &Builtin::View(style) => container.layout().unwrap().set_style(style),
162            Builtin::Window => {
163                let window = if let Some(window) = container.downcast_mut::<PLYWindow>() {
164                    window
165                } else {
166                    return;
167                };
168
169                let frame = window.root_view_controller().view_mut().frame();
170
171                container.layout().unwrap().set_style(ViewStyle {
172                    position: Position::Relative(Relative {
173                        flex_shrink: 0.0,
174                        flex_grow: 0.0,
175                        ..Default::default()
176                    }),
177                    flex_direction: FlexDirection::Column,
178                    size: Size {
179                        width: Dimension::Points(frame.size.width as _),
180                        height: Dimension::Points(frame.size.height as _),
181                    },
182                    ..Default::default()
183                });
184            }
185            _ => {}
186        }
187    }
188}