Skip to main content

polyhorn_ios/raw/
compositor.rs

1use dispatch::Queue;
2use polyhorn_core::{Command, Composition};
3use polyhorn_ui::layout::LayoutTree;
4use std::sync::atomic::{AtomicUsize, Ordering};
5use std::sync::{Arc, RwLock};
6
7use super::{Environment, OpaqueContainer, Platform, QueueBound};
8
9/// Concrete implementation of a compositor that is responsible for adding and
10/// removing native views from the native view hierarchy based on the virtual
11/// representation within Polyhorn.
12#[derive(Clone)]
13pub struct Compositor {
14    buffer: Arc<QueueBound<Composition<Platform>>>,
15    counter: Arc<AtomicUsize>,
16    layout_tree: Arc<RwLock<LayoutTree>>,
17}
18
19impl Compositor {
20    /// Returns a new compositor with the given shared layout tree.
21    pub fn new(layout_tree: Arc<RwLock<LayoutTree>>) -> Compositor {
22        Compositor {
23            buffer: Arc::new(QueueBound::new(Queue::main(), || Default::default())),
24            counter: Arc::new(AtomicUsize::default()),
25            layout_tree,
26        }
27    }
28
29    fn next_id(&mut self) -> ContainerID {
30        let id = self.counter.fetch_add(1, Ordering::Relaxed);
31        ContainerID(id)
32    }
33
34    pub(crate) fn track(&mut self, container: OpaqueContainer) -> ContainerID {
35        let id = self.next_id();
36
37        unsafe {
38            self.buffer.with_adopt(container, move |state, container| {
39                state.insert(id, container);
40            });
41        }
42
43        id
44    }
45}
46
47impl polyhorn_core::Compositor<Platform> for Compositor {
48    fn buffer(&self) -> CommandBuffer {
49        CommandBuffer {
50            compositor: self.clone(),
51            commands: vec![],
52        }
53    }
54}
55
56/// An opaque ID for containers that can be shared between threads.
57#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
58pub struct ContainerID(usize);
59
60/// Concrete implementation of a command buffer that can buffer commands before
61/// committing them to the compositor.
62pub struct CommandBuffer {
63    compositor: Compositor,
64    commands: Vec<Command<Platform>>,
65}
66
67impl polyhorn_core::CommandBuffer<Platform> for CommandBuffer {
68    fn mount<F>(&mut self, parent_id: ContainerID, initializer: F) -> ContainerID
69    where
70        F: FnOnce(&mut OpaqueContainer, &mut Environment) -> OpaqueContainer + Send + 'static,
71    {
72        let id = self.compositor.next_id();
73        self.commands
74            .push(Command::Mount(id, parent_id, Box::new(initializer)));
75        id
76    }
77
78    fn mutate<F>(&mut self, ids: &[ContainerID], mutator: F)
79    where
80        F: FnOnce(&mut [&mut OpaqueContainer], &mut Environment) + Send + 'static,
81    {
82        self.commands
83            .push(Command::Mutate(ids.to_owned(), Box::new(mutator)));
84    }
85
86    fn unmount(&mut self, id: ContainerID) {
87        self.commands.push(Command::Unmount(id));
88    }
89
90    fn layout(&mut self) {
91        self.mutate(&[], |_, environment| {
92            let mut layout_tree = environment.layout_tree().write().unwrap();
93            layout_tree.recompute_roots();
94        });
95    }
96
97    fn commit(mut self) {
98        let commands = std::mem::take(&mut self.commands);
99
100        let layout_tree = self.compositor.layout_tree.clone();
101
102        self.compositor.buffer.with(move |state| {
103            // Apply each command to this state.
104            let mut environment = Environment::new(layout_tree.clone());
105            for command in commands {
106                state.process(&mut environment, command);
107            }
108        });
109    }
110}