use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashMap;
use ui_core::LayoutItem;
pub type SurfaceContent = Rc<dyn Fn() -> Box<dyn LayoutItem>>;
pub fn surface_content(build: impl Fn() -> Box<dyn LayoutItem> + 'static) -> SurfaceContent {
Rc::new(build)
}
pub trait SurfaceControl {
fn close(&self);
fn is_closing(&self) -> bool;
fn rebuild(&self) {}
}
pub trait SurfaceHost<P: 'static>: 'static {
fn open(&self, placement: P, content: SurfaceContent) -> SurfaceToken;
}
pub struct SurfaceToken {
control: Box<dyn SurfaceControl>,
}
impl SurfaceToken {
pub fn new(control: Box<dyn SurfaceControl>) -> Self {
Self { control }
}
pub fn close(&self) {
self.control.close();
}
pub fn is_closing(&self) -> bool {
self.control.is_closing()
}
pub fn rebuild(&self) {
self.control.rebuild();
}
}
impl Drop for SurfaceToken {
fn drop(&mut self) {
self.control.close();
}
}
thread_local! {
static SURFACE_HOSTS: RefCell<HashMap<TypeId, Box<dyn Any>>> =
RefCell::new(HashMap::new());
}
pub fn set_surface_host<P: 'static>(host: impl SurfaceHost<P>) {
let host: Box<dyn SurfaceHost<P>> = Box::new(host);
SURFACE_HOSTS.with(|hosts| {
hosts
.borrow_mut()
.insert(TypeId::of::<P>(), Box::new(host) as Box<dyn Any>)
});
}
pub fn open_surface<P: 'static>(placement: P, content: SurfaceContent) -> SurfaceToken {
SURFACE_HOSTS.with(|hosts| {
let hosts = hosts.borrow();
match hosts
.get(&TypeId::of::<P>())
.and_then(|host| host.downcast_ref::<Box<dyn SurfaceHost<P>>>())
{
Some(host) => host.open(placement, content),
None => {
tracing::warn!(
"telar::open_surface: no SurfaceHost installed on this thread for {}; surface ignored",
std::any::type_name::<P>()
);
SurfaceToken::new(Box::new(NoopControl))
}
}
})
}
struct NoopControl;
impl SurfaceControl for NoopControl {
fn close(&self) {}
fn is_closing(&self) -> bool {
true
}
}