use std::cell::RefCell;
use std::panic::{self, AssertUnwindSafe};
use std::rc::Rc;
use crabshell_reactive::{ReadSignal, Signal, untrack};
use crate::cx::{Anchor, Cx};
use crate::input::panic_message;
use crate::prop::{IntoProp, Prop, PropKind};
use crate::style::Style;
use crate::tree::{Tree, borrow};
use crate::widgets::Widget;
use crate::window::{Output, Window};
type Build = Rc<dyn Fn(&Cx, &Output) -> Box<dyn Widget>>;
type Parts = (Option<Rc<dyn Fn() -> Style>>, Option<Build>);
#[derive(Default)]
pub struct SessionLock {
locked: Option<Prop<bool>>,
style: Option<Prop<Style>>,
surface: Option<Build>,
}
impl SessionLock {
pub fn new() -> SessionLock {
SessionLock::default()
}
pub fn locked(mut self, locked: impl IntoProp<bool>) -> SessionLock {
self.locked = Some(locked.into_prop());
self
}
pub fn style(mut self, style: impl IntoProp<Style>) -> SessionLock {
self.style = Some(style.into_prop());
self
}
pub fn surface<W: Widget>(mut self, build: impl Fn(&Cx, &Output) -> W + 'static) -> SessionLock {
self.surface = Some(Rc::new(move |cx: &Cx, output: &Output| {
Box::new(build(cx, output)) as Box<dyn Widget>
}));
self
}
}
pub struct Lock {
cx: Cx,
wanted: Signal<bool>,
locked: Signal<bool>,
parts: Signal<Parts>,
}
impl Lock {
pub fn new(cx: &Cx, lock: SessionLock) -> Lock {
let cx = cx.child().named("SessionLock");
let wanted = cx.signal(false);
match lock.locked.map(|locked| locked.0) {
None => {}
Some(PropKind::Value(value)) => wanted.set(value),
Some(PropKind::Binding(mut compute)) => {
cx.effect(move || wanted.set(compute()))
.named("SessionLock.locked");
}
}
Lock {
wanted,
locked: cx.signal(false),
parts: cx.signal((lock.style.map(Prop::shared), lock.surface)),
cx,
}
}
pub fn wanted(&self) -> Option<bool> {
untrack(|| self.wanted.try_get())
}
pub fn locked(&self) -> ReadSignal<bool> {
self.locked.read_only()
}
pub fn set_locked(&self, locked: bool) {
if untrack(|| self.locked.try_get()) == Some(!locked) {
self.locked.set(locked);
}
}
pub fn surface(&self, output: &Output) -> Option<Window> {
let (style, build) = untrack(|| self.parts.try_with(Clone::clone))?;
let path: Rc<str> = format!("SessionLock[{}]", output.name()).into();
let cx = self
.cx
.child()
.named(path.to_string())
.at(Rc::new(Anchor::default()));
let built = panic::catch_unwind(AssertUnwindSafe(|| {
untrack(|| build.map(|build| build(&cx, output).into_element()))
}));
let child = built.unwrap_or_else(|payload| {
tracing::error!(
"{path} panicked while it was built, so it shows only the lock's style: {}",
panic_message(payload.as_ref())
);
None
});
let owner = cx.owner();
let tree = Rc::new_cyclic(|this| {
RefCell::new(Tree::new(
this.clone(),
"SessionLock",
output.clone(),
cx,
path,
))
});
let style = style.map(|style| (move || style()).into_prop());
borrow(&tree).mount_root(style, child);
Some(Window { tree, owner })
}
}
impl Drop for Lock {
fn drop(&mut self) {
self.cx.owner().dispose();
}
}