use blinc_core::reactive::SignalId;
use blinc_layout::binding::Reactive;
pub fn current<T>(r: &Reactive<T>) -> T
where
T: Clone + Send + Default + 'static,
{
match r {
Reactive::Const(v) => v.clone(),
Reactive::Bound(state) => state.try_get().unwrap_or_default(),
Reactive::Computed(computed) => computed.try_get().unwrap_or_default(),
}
}
pub fn dep_signal<T: Clone + Send + 'static>(r: &Reactive<T>) -> Option<SignalId> {
match r {
Reactive::Bound(state) => Some(state.signal_id()),
Reactive::Const(_) | Reactive::Computed(_) => None,
}
}
pub fn clone_reactive<T: Clone>(r: &Reactive<T>) -> Reactive<T> {
match r {
Reactive::Const(v) => Reactive::Const(v.clone()),
Reactive::Bound(state) => Reactive::Bound(state.clone()),
Reactive::Computed(computed) => Reactive::Computed(computed.clone()),
}
}
pub fn reactive_node<F>(
src: &Reactive<String>,
build: F,
) -> Box<dyn blinc_layout::div::ElementBuilder>
where
F: Fn(String) -> Box<dyn blinc_layout::div::ElementBuilder> + Send + Sync + 'static,
{
use blinc_core::reactive::{ReactiveGraph, computed};
let Reactive::Bound(state) = src else {
return build(current(src));
};
let signal = state.signal();
let value = computed(move |g: &ReactiveGraph| vec![g.get(signal).unwrap_or_default()]);
Box::new(
blinc_layout::div::div()
.w_fit()
.h_fit()
.align_self_center()
.for_each(&value, |v: &String| v.clone(), move |v: String| build(v)),
)
}
pub fn reactive_text<F>(
src: &Reactive<String>,
style: F,
) -> Box<dyn blinc_layout::div::ElementBuilder>
where
F: FnOnce(blinc_layout::text::Text) -> blinc_layout::text::Text,
{
Box::new(style(blinc_layout::text::Text::bound(clone_reactive(src))))
}
static BOUND: std::sync::Mutex<Option<std::collections::HashSet<(u64, u64)>>> =
std::sync::Mutex::new(None);
pub struct BoolTarget {
pub anim: blinc_layout::motion::SharedAnimatedValue,
pub on: f32,
pub off: f32,
}
impl BoolTarget {
pub fn to(anim: &blinc_layout::motion::SharedAnimatedValue, on: f32) -> Self {
Self {
anim: anim.clone(),
on,
off: 0.0,
}
}
}
pub fn bind_bool_targets(
kind: u64,
signal: blinc_core::reactive::Signal<bool>,
targets: Vec<BoolTarget>,
) {
{
let mut guard = BOUND.lock().unwrap_or_else(|e| e.into_inner());
let seen = guard.get_or_insert_with(std::collections::HashSet::new);
if !seen.insert((signal.id().to_raw(), kind)) {
return;
}
}
blinc_core::owner::Owner::detached(|| {
blinc_core::reactive::effect(move |graph| {
let on = graph.get(signal).unwrap_or(false);
for t in &targets {
t.anim
.lock()
.unwrap()
.set_target(if on { t.on } else { t.off });
}
});
});
}
pub mod bool_binding {
pub const SWITCH: u64 = 1;
pub const COLLAPSIBLE: u64 = 2;
}
#[derive(Clone, Copy)]
pub enum NumberValue {
F32(blinc_core::reactive::Signal<f32>),
F64(blinc_core::reactive::Signal<f64>),
}
impl NumberValue {
pub fn get(&self) -> f32 {
match self {
Self::F32(s) => s.try_get().unwrap_or_default(),
Self::F64(s) => s.try_get().unwrap_or_default() as f32,
}
}
pub fn read(&self, g: &blinc_core::reactive::ReactiveGraph) -> f32 {
match *self {
Self::F32(s) => g.get(s).unwrap_or_default(),
Self::F64(s) => g.get(s).unwrap_or_default() as f32,
}
}
pub fn set(&self, value: f32) {
match self {
Self::F32(s) => s.set(value),
Self::F64(s) => s.set(value as f64),
}
}
pub fn signal_id(&self) -> blinc_core::reactive::SignalId {
match self {
Self::F32(s) => s.id(),
Self::F64(s) => s.id(),
}
}
}
impl From<blinc_core::reactive::Signal<f32>> for NumberValue {
fn from(s: blinc_core::reactive::Signal<f32>) -> Self {
Self::F32(s)
}
}
impl From<blinc_core::reactive::Signal<f64>> for NumberValue {
fn from(s: blinc_core::reactive::Signal<f64>) -> Self {
Self::F64(s)
}
}
pub(crate) fn non_empty(src: &Reactive<String>) -> Reactive<bool> {
match src {
Reactive::Const(s) => Reactive::Const(!s.is_empty()),
bound => {
let bound = bound.clone();
Reactive::Computed(blinc_core::reactive::computed(move |g| {
!bound.read(g).unwrap_or_default().is_empty()
}))
}
}
}
pub(crate) fn field_message(
error: &Option<Reactive<String>>,
description: &Option<Reactive<String>>,
size: f32,
) -> Option<blinc_layout::div::Div> {
use blinc_layout::text::Text;
let text = |src: &Reactive<String>, class: &'static str| {
let src = src.clone();
move || Text::bound(src.clone()).class(class).size(size)
};
let row = blinc_layout::div::div().class("cn-field-message");
match (error, description) {
(None, None) => None,
(None, Some(desc)) => Some(row.child(text(desc, "cn-field-description")())),
(Some(err), None) => Some(row.show(non_empty(err), text(err, "cn-field-error"))),
(Some(err), Some(desc)) => Some(row.show_or(
non_empty(err),
text(err, "cn-field-error"),
text(desc, "cn-field-description"),
)),
}
}