polyhorn_android/raw/
container.rs

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