use std::{cell::RefCell, rc::Rc};
use cranpose::{
native_view::{NativeView as Slot, NativeViewHost},
prelude::Modifier,
};
use cranpose_core::{CompositionLocal, CompositionLocalProvider, SideEffect, compositionLocalOf};
use crate::NativeEvent;
thread_local! {
static CONTEXT: CompositionLocal<Option<NativeContext>> = compositionLocalOf(|| None);
}
#[derive(Clone)]
pub(crate) struct NativeContext {
pub(crate) views: NativeViewHost,
events: Rc<RefCell<Vec<NativeEvent>>>,
}
impl PartialEq for NativeContext {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.events, &other.events)
}
}
impl NativeContext {
pub(crate) fn new() -> Self {
Self {
views: NativeViewHost::default(),
events: Rc::default(),
}
}
pub(crate) fn provide(&self, content: impl FnOnce()) {
CONTEXT.with(|local| {
CompositionLocalProvider([local.provides(Some(self.clone()))], || {
self.views.provide(content);
});
});
}
pub(crate) fn take_events(&self) -> Vec<NativeEvent> {
std::mem::take(&mut *self.events.borrow_mut())
}
fn current() -> Self {
CONTEXT
.with(CompositionLocal::current)
.expect("native content requires a NativeSession")
}
}
pub struct NativeContent {
pub(crate) draw: Box<dyn FnMut()>,
pub(crate) event: Box<dyn FnMut(NativeEvent)>,
}
impl NativeContent {
pub fn new(content: impl FnMut() + 'static) -> Self {
Self {
draw: Box::new(content),
event: Box::new(|_| {}),
}
}
pub fn on_event(mut self, event: impl FnMut(NativeEvent) + 'static) -> Self {
self.event = Box::new(event);
self
}
}
#[cranpose::prelude::composable]
pub fn SendToHost(name: impl Into<String>, value: impl Into<String>) {
let context = NativeContext::current();
let event = NativeEvent {
name: name.into(),
value: value.into(),
};
SideEffect(move || context.events.borrow_mut().push(event));
}
#[cranpose::prelude::composable]
pub fn NativeView(kind: &str, value: &str, modifier: Modifier, on_event: impl Fn(&str) + 'static) {
Slot(
NativeContext::current().views,
kind,
value,
modifier,
on_event,
);
}