use std::{cell::RefCell, cmp::Ordering, rc::Rc};
use web_sys::{
js_sys::Function,
wasm_bindgen::{prelude::Closure, JsValue},
TouchEvent, WheelEvent,
};
use yew::Context;
use crate::{ScrollMotion, TerminalApp, WebTermMessage, WebTerminal};
pub(crate) fn process_resize_event<A: TerminalApp>(ctx: &Context<WebTerminal<A>>) -> Function {
let cb = ctx.link().callback(|()| WebTermMessage::Resized);
let func = move || cb.emit(());
Closure::<dyn 'static + Fn()>::new(func)
.into_js_value()
.into()
}
pub(crate) fn process_wheel_event<A: TerminalApp>(ctx: &Context<WebTerminal<A>>) -> Function {
let cb = ctx.link().callback(|msg: WebTermMessage<A::Message>| msg);
let func = move |event: JsValue| {
let event: WheelEvent = event.into();
match event.delta_y().partial_cmp(&0.0) {
Some(Ordering::Less) => cb.emit(WebTermMessage::Scrolled(ScrollMotion::Down)),
Some(Ordering::Greater) => cb.emit(WebTermMessage::Scrolled(ScrollMotion::Up)),
_ => {}
}
};
Closure::<dyn 'static + Fn(JsValue)>::new(func)
.into_js_value()
.into()
}
pub(crate) fn process_touch_init_event(acc: Rc<RefCell<TouchScroll>>) -> Function {
let func = move |event: JsValue| {
let event: TouchEvent = event.into();
if let Some(touch) = event.touches().get(0) {
acc.borrow_mut().init_touch(&touch);
}
};
Closure::<dyn 'static + Fn(JsValue)>::new(func)
.into_js_value()
.into()
}
pub(crate) fn process_touch_move_event<A: TerminalApp>(
ctx: &Context<WebTerminal<A>>,
acc: Rc<RefCell<TouchScroll>>,
) -> Function {
let cb = ctx.link().callback(|msg: WebTermMessage<A::Message>| msg);
let func = move |event: JsValue| {
let event: TouchEvent = event.into();
if let Some(touch) = event.touches().get(0) {
acc.borrow_mut()
.add_touch(&touch)
.for_each(|scroll| cb.emit(WebTermMessage::Scrolled(scroll)));
}
};
Closure::<dyn 'static + Fn(JsValue)>::new(func)
.into_js_value()
.into()
}
use web_sys::Touch;
#[derive(Debug, Default, Clone)]
pub(crate) struct TouchScroll {
last: Position,
acc: i32,
}
impl TouchScroll {
const SCROLL_THRES: usize = 20;
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn init_touch(&mut self, event: &Touch) {
let pos = Position::new(event);
self.last = pos;
self.acc = 0;
}
pub(crate) fn add_touch(&mut self, event: &Touch) -> impl Iterator<Item = ScrollMotion> {
let pos = Position::new(event);
if self.last.is_connected(pos) {
self.acc += self.last.y - pos.y;
let digest = self.acc / Self::SCROLL_THRES as i32;
let rem = self.acc.abs() % Self::SCROLL_THRES as i32;
let val = if self.acc > 0 {
self.acc = rem;
ScrollMotion::Up
} else {
self.acc = -rem;
ScrollMotion::Down
};
self.last = pos;
std::iter::repeat(val).take(digest.unsigned_abs() as usize)
} else {
std::iter::repeat(ScrollMotion::Down).take(0)
}
}
}
#[derive(Debug, Default, Clone, Copy)]
struct Position {
x: i32,
y: i32,
}
impl Position {
const CONNECT_THRES: usize = 200;
fn new(event: &Touch) -> Self {
Self {
x: event.page_x(),
y: event.page_y(),
}
}
fn is_connected(&self, pos: Position) -> bool {
((((self.x - pos.x).pow(2) + (self.y - pos.y).pow(2)) as f64).sqrt() as usize)
<= Self::CONNECT_THRES
}
}