use cranpose_core::compositionLocalOfWithPolicy;
use cranpose_core::CompositionLocal;
use cranpose_core::CompositionLocalProvider;
use cranpose_macros::composable;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum HapticFeedback {
ImpactLight,
ImpactMedium,
ImpactHeavy,
Selection,
Success,
Warning,
Error,
}
pub trait Haptics {
fn perform(&self, feedback: HapticFeedback);
}
pub type HapticsRef = Rc<dyn Haptics>;
struct NoopHaptics;
impl Haptics for NoopHaptics {
fn perform(&self, _feedback: HapticFeedback) {}
}
thread_local! {
static PLATFORM_HAPTICS: RefCell<Option<HapticsRef>> = const { RefCell::new(None) };
}
pub fn set_platform_haptics(haptics: HapticsRef) {
PLATFORM_HAPTICS.with(|cell| *cell.borrow_mut() = Some(haptics));
}
pub fn clear_platform_haptics() {
PLATFORM_HAPTICS.with(|cell| *cell.borrow_mut() = None);
}
pub fn default_haptics() -> HapticsRef {
PLATFORM_HAPTICS
.with(|cell| cell.borrow().clone())
.unwrap_or_else(|| Rc::new(NoopHaptics))
}
pub fn local_haptics() -> CompositionLocal<HapticsRef> {
thread_local! {
static LOCAL_HAPTICS: RefCell<Option<CompositionLocal<HapticsRef>>> = const { RefCell::new(None) };
}
LOCAL_HAPTICS.with(|cell| {
let mut local = cell.borrow_mut();
local
.get_or_insert_with(|| compositionLocalOfWithPolicy(default_haptics, Rc::ptr_eq))
.clone()
})
}
#[allow(non_snake_case)]
#[composable]
pub fn ProvideHaptics(content: impl FnOnce()) {
let haptics = cranpose_core::remember(default_haptics).with(|state| state.clone());
let local = local_haptics();
CompositionLocalProvider(vec![local.provides(haptics)], move || {
content();
});
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
#[test]
fn registered_haptics_receives_events() {
clear_platform_haptics();
default_haptics().perform(HapticFeedback::Selection);
struct Rec(Rc<Cell<u32>>);
impl Haptics for Rec {
fn perform(&self, _f: HapticFeedback) {
self.0.set(self.0.get() + 1);
}
}
let count = Rc::new(Cell::new(0));
set_platform_haptics(Rc::new(Rec(count.clone())));
default_haptics().perform(HapticFeedback::ImpactMedium);
assert_eq!(count.get(), 1);
clear_platform_haptics();
}
}