use super::*;
type SignalUpdateHandler<T> = Rc<dyn Fn(nami::watcher::Context<T>)>;
struct SubscribedSnapshotState<T> {
pending: Vec<nami::watcher::Context<T>>,
handler: Option<SignalUpdateHandler<T>>,
}
pub(super) struct SubscribedSnapshot<T, G> {
guard: G,
state: Rc<RefCell<SubscribedSnapshotState<T>>>,
}
impl<T: 'static, G> SubscribedSnapshot<T, G> {
pub(super) fn new<S>(signal: &S) -> (Self, T)
where
S: Signal<Output = T, Guard = G>,
{
let state = Rc::new(RefCell::new(SubscribedSnapshotState {
pending: Vec::new(),
handler: None,
}));
let guard = signal.watch({
let state = Rc::clone(&state);
move |update| {
let handler = state.borrow().handler.clone();
if let Some(handler) = handler {
handler(update);
} else {
state.borrow_mut().pending.push(update);
}
}
});
let snapshot = signal.get();
(Self { guard, state }, snapshot)
}
pub(super) fn activate(self, handler: impl Fn(nami::watcher::Context<T>) + 'static) -> G {
let handler: SignalUpdateHandler<T> = Rc::new(handler);
let pending = {
let mut state = self.state.borrow_mut();
state.handler = Some(Rc::clone(&handler));
core::mem::take(&mut state.pending)
};
for update in pending {
handler(update);
}
self.guard
}
}
impl HydrolysisRenderer {
pub(super) fn watch_signal<S>(&mut self, signal: &S)
where
S: Signal + Clone + 'static,
{
let Some(identity) = signal.identity() else {
let signals = self.signals.clone();
let guard = signal.watch(move |_| signals.request_refresh());
self.lifecycle.current_frame_retain.push(Retain::new(guard));
return;
};
let key = identity.raw();
let signal_type = core::any::TypeId::of::<S>();
if self.lifecycle.signal_watches.mark_seen(key, signal_type) {
return;
}
let signals = self.signals.clone();
let guard = signal.watch(move |_| signals.request_refresh());
self.lifecycle.signal_watches.insert(
key,
signal_type,
Box::new(signal.clone()),
Retain::new(guard),
);
}
pub(crate) fn read_signal<S>(&mut self, signal: &S) -> S::Output
where
S: Signal + Clone + 'static,
{
self.watch_signal(signal);
signal.get()
}
pub(crate) fn read_resolved_text_styled(
&mut self,
text: &Text,
env: &Environment,
) -> StyledStr {
let resolved = text.resolve(env);
self.read_signal(&resolved.content)
}
pub(crate) fn set_frame_instant(&mut self, at: Instant) {
self.frame_instant = at;
self.signals.set_frame_clock(at);
}
pub(crate) fn frame_instant(&self) -> Instant {
self.frame_instant
}
pub(crate) fn resolve_toggle_progress<S>(
&mut self,
signal: &S,
default_animation: Animation,
) -> (f32, bool)
where
S: Signal<Output = bool> + Clone + 'static,
{
let Some(identity) = signal.identity() else {
let selected = self.read_signal(signal);
return (if selected { 1.0 } else { 0.0 }, selected);
};
let (subscription, selected) = SubscribedSnapshot::new(signal);
let now = self.frame_instant;
let target = if selected { 1.0 } else { 0.0 };
let key = AnimationKey::scalar(identity);
let handle = self.animation_controller.bind_scalar_target(
key,
target,
default_animation.clone(),
now,
);
let watcher_handle = handle.clone();
let signals = self.signals.clone();
let guard = subscription.activate(move |update| {
let target = if *update.value() { 1.0 } else { 0.0 };
let animation = update
.metadata()
.try_get::<Animation>()
.unwrap_or_else(|| default_animation.clone());
watcher_handle.apply_target(target, Some(animation), signals.frame_clock());
signals.request_refresh();
});
self.lifecycle.current_frame_retain.push(Retain::new(guard));
(handle.sample(now).clamp(0.0, 1.0), selected)
}
pub(crate) fn sample_widget_scalar_target(
&mut self,
key: AnimationKey,
target: f32,
animation: Animation,
) -> f32 {
let now = self.frame_instant;
self.animation_controller
.bind_scalar_target(key, target, animation, now)
.sample(now)
}
pub(crate) fn sample_radio_indicator_state(
&mut self,
key: AnimationKey,
selected: bool,
motion: &RadioSelectionMotion,
) -> RadioIndicatorState {
self.animation_controller
.bind_radio_indicator(key, selected, motion, self.frame_instant)
}
pub(crate) fn sample_repeating_motion(&mut self, cycle: Duration, node_id: usize) -> Duration {
let key = AnimationKey::renderer_local_repeating(node_id);
self.animation_controller
.bind_repeating_phase(key, cycle, self.frame_instant)
}
pub fn advance_animations(&mut self) -> bool {
let now = self.frame_instant;
let pending_releases = self.flush_interaction_releases(now);
self.animation_controller.tick(now)
|| pending_releases
|| self.navigation.slots.values().any(|slot| {
slot.transition
.as_ref()
.is_some_and(|state| state.is_active(now))
|| slot
.interactive_pop
.as_ref()
.is_some_and(NavigationInteractivePop::is_animating)
})
}
pub fn animations_active(&self) -> bool {
let now = self.frame_instant;
self.animation_controller.has_active(now)
|| self.has_pending_interaction_releases(now)
|| self.navigation.slots.values().any(|slot| {
slot.transition
.as_ref()
.is_some_and(|state| state.is_active(now))
|| slot
.interactive_pop
.as_ref()
.is_some_and(NavigationInteractivePop::is_animating)
})
}
}