Skip to main content

polyhorn_ios/raw/
container.rs

1use as_any::AsAny;
2use polyhorn_ios_sys::polykit::{PLYView, PLYViewController, PLYWindow};
3use polyhorn_ios_sys::uikit::UIApplication;
4use polyhorn_ui::layout::LayoutNode;
5
6use super::{Environment, Platform};
7
8/// Concrete implementation of a iOS-specific container.
9pub trait Container: AsAny + 'static {
10    /// This function should mount the given child container onto this container.
11    fn mount(&mut self, child: &mut OpaqueContainer);
12
13    /// This function should unmount this container from its parent container.
14    fn unmount(&mut self);
15
16    /// This optional function upcasts the native representation of this
17    /// container to a `UIView`.
18    fn to_view(&self) -> Option<PLYView> {
19        None
20    }
21
22    /// This optional function upcasts the native representation of this
23    /// container to a `UIViewController`.
24    fn to_view_controller(&self) -> Option<PLYViewController> {
25        None
26    }
27
28    /// This optional function upcasts the native representation of this
29    /// container to a `UIWindow`.
30    fn to_window(&self) -> Option<PLYWindow> {
31        None
32    }
33}
34
35/// Opaque wrapper around a container with two optional layouts attached. If
36/// both are given, the first refers to the container's layout itself, whereas
37/// the second refers to the container's content layout. These can be different
38/// when working with scroll views for example, which are essentially treated
39/// as two adjacent nodes in the layout tree.
40pub struct OpaqueContainer(Option<LayoutNode>, Option<LayoutNode>, Box<dyn Container>);
41
42impl OpaqueContainer {
43    /// Returns a new opaque container with the given layout, view and
44    /// optionally a separate content layout.
45    pub fn new<T>(
46        layout: LayoutNode,
47        content_layout: Option<LayoutNode>,
48        view: T,
49    ) -> OpaqueContainer
50    where
51        T: Container,
52    {
53        OpaqueContainer(Some(layout), content_layout, Box::new(view))
54    }
55
56    /// Returns a new opaque container for the root of the UI hierarchy (which
57    /// on iOS is `UIApplication`).
58    pub fn root() -> OpaqueContainer {
59        OpaqueContainer(None, None, Box::new(UIApplication::shared()))
60    }
61
62    /// Returns the layout of this container (if applicable).
63    pub fn layout(&self) -> Option<&LayoutNode> {
64        self.0.as_ref()
65    }
66
67    /// Returns the content layout of this container (if applicable). Returns
68    /// `None` if not applicable, even if the container has a regular layout.
69    pub fn content_layout(&self) -> Option<&LayoutNode> {
70        self.1.as_ref()
71    }
72
73    /// Returns the container wrapped in this opaque container.
74    pub fn container(&self) -> &dyn Container {
75        self.2.as_ref()
76    }
77
78    /// Attempts to downcast this container to a concrete type and if
79    /// successful, returns a mutable reference.
80    pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
81    where
82        T: 'static,
83    {
84        self.2.as_mut().as_any_mut().downcast_mut::<T>()
85    }
86}
87
88impl polyhorn_core::Container<Platform> for OpaqueContainer {
89    fn mount(&mut self, container: &mut OpaqueContainer, _environment: &mut Environment) {
90        if let (Some(child), Some(content)) = (container.layout(), container.content_layout()) {
91            let mut layouter = child.layouter().write().unwrap();
92            layouter.add_child(child.node(), content.node());
93        }
94
95        if let (Some(parent), Some(child)) = (
96            self.content_layout().or_else(|| self.layout()),
97            container.layout(),
98        ) {
99            let mut layouter = parent.layouter().write().unwrap();
100            layouter.add_child(parent.node(), child.node());
101        }
102
103        self.2.mount(container);
104    }
105
106    fn unmount(&mut self) {
107        if let Some(layout) = self.content_layout() {
108            let mut layouter = layout.layouter().write().unwrap();
109            layouter.remove(layout.node());
110        }
111
112        if let Some(layout) = self.layout() {
113            let mut layouter = layout.layouter().write().unwrap();
114            layouter.remove(layout.node());
115        }
116
117        self.2.unmount();
118    }
119}