1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use as_any::AsAny;
use polyhorn_android_sys::{Activity, View};
use polyhorn_ui::layout::LayoutNode;

use super::{Environment, Platform};

/// Concrete implementation of an Android-specific container.
pub trait Container: AsAny + Send {
    /// This function should mount the given child container onto this container.
    fn mount(&mut self, child: &mut OpaqueContainer, environment: &mut Environment);

    /// This function should unmount this container from its parent container.
    fn unmount(&mut self);

    fn to_view(&self) -> Option<View> {
        None
    }
}

/// Opaque wrapper around a container with two optional layouts attached. If
/// both are given, the first refers to the container's layout itself, whereas
/// the second refers to the container's content layout. These can be different
/// when working with scroll views for example, which are essentially treated
/// as two adjacent nodes in the layout tree.
pub struct OpaqueContainer(Option<LayoutNode>, Option<LayoutNode>, Box<dyn Container>);

impl OpaqueContainer {
    /// Returns a new opaque container with the given layout, view and
    /// optionally a separate content layout.
    pub fn new<T>(
        layout: LayoutNode,
        content_layout: Option<LayoutNode>,
        view: T,
    ) -> OpaqueContainer
    where
        T: Container,
    {
        OpaqueContainer(Some(layout), content_layout, Box::new(view))
    }

    pub unsafe fn activity(
        env: *mut std::ffi::c_void,
        object: *mut std::ffi::c_void,
    ) -> OpaqueContainer {
        OpaqueContainer(
            None,
            None,
            Box::new(Activity::with_env(env as _, object as _)),
        )
    }

    /// Returns the layout of this container (if applicable).
    pub fn layout(&self) -> Option<&LayoutNode> {
        self.0.as_ref()
    }

    /// Returns the content layout of this container (if applicable). Returns
    /// `None` if not applicable, even if the container has a regular layout.
    pub fn content_layout(&self) -> Option<&LayoutNode> {
        self.1.as_ref()
    }

    /// Returns the container wrapped in this opaque container.
    pub fn container(&self) -> &dyn Container {
        self.2.as_ref()
    }

    /// Attempts to downcast this container to a concrete type and if
    /// successful, returns a mutable reference.
    pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
    where
        T: 'static,
    {
        self.2.as_mut().as_any_mut().downcast_mut::<T>()
    }
}

impl polyhorn_core::Container<Platform> for OpaqueContainer {
    fn mount(&mut self, container: &mut OpaqueContainer, environment: &mut Environment) {
        if let (Some(child), Some(content)) = (container.layout(), container.content_layout()) {
            let mut layouter = child.layouter().write().unwrap();
            layouter.add_child(child.node(), content.node());
        }

        if let (Some(parent), Some(child)) = (
            self.content_layout().or_else(|| self.layout()),
            container.layout(),
        ) {
            let mut layouter = parent.layouter().write().unwrap();
            layouter.add_child(parent.node(), child.node());
        }

        self.2.mount(container, environment);
    }

    fn unmount(&mut self) {
        if let Some(layout) = self.content_layout() {
            let mut layouter = layout.layouter().write().unwrap();
            layouter.remove(layout.node());
        }

        if let Some(layout) = self.layout() {
            let mut layouter = layout.layouter().write().unwrap();
            layouter.remove(layout.node());
        }

        self.2.unmount();
    }
}