use gpui::{
BackGestureEvent, BackGesturePhase, Modifiers, Pixels, Point, ScrollDelta, ScrollEvent,
ScrollSource, TouchId, TouchPhase, px,
};
use objc2_core_foundation::CGPoint;
use objc2_ui_kit::{
UIGestureRecognizerState, UIKeyModifierFlags, UIPanGestureRecognizer, UIRectEdge,
UIScreenEdgePanGestureRecognizer, UITouch, UITouchPhase, UIView,
};
const BACK_COMMIT_VELOCITY: f64 = 300.;
pub fn back_event(
recognizer: &UIScreenEdgePanGestureRecognizer,
view: &UIView,
start_position: Point<Pixels>,
) -> Option<BackGestureEvent> {
let direction = if recognizer.edges() == UIRectEdge::Right {
-1.
} else {
1.
};
let width = view.bounds().size.width.max(1.);
let travel = recognizer.translationInView(Some(view)).x * direction;
let progress = (travel / width).clamp(0., 1.);
let phase = match recognizer.state() {
UIGestureRecognizerState::Began | UIGestureRecognizerState::Changed => {
BackGesturePhase::Progressed
}
UIGestureRecognizerState::Ended => {
let velocity = recognizer.velocityInView(Some(view)).x * direction;
if velocity > BACK_COMMIT_VELOCITY
|| (progress > 0.5 && velocity > -BACK_COMMIT_VELOCITY)
{
BackGesturePhase::Committed
} else {
BackGesturePhase::Cancelled
}
}
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
BackGesturePhase::Cancelled
}
_ => return None,
};
Some(BackGestureEvent {
phase,
start_position,
progress: progress as f32,
})
}
pub fn touch_location_in_view(touch: &UITouch, view: &UIView) -> Point<Pixels> {
let location = touch.locationInView(Some(view));
Point::new(px(location.x as f32), px(location.y as f32))
}
pub fn touch_phase(touch: &UITouch) -> TouchPhase {
match touch.phase() {
UITouchPhase::Began => TouchPhase::Started,
UITouchPhase::Moved | UITouchPhase::Stationary => TouchPhase::Moved,
UITouchPhase::Ended => TouchPhase::Ended,
_ => TouchPhase::Cancelled,
}
}
pub fn touch_id(touch: &UITouch) -> TouchId {
TouchId(touch as *const UITouch as usize as u64)
}
pub fn scroll_event(recognizer: &UIPanGestureRecognizer, view: &UIView) -> Option<ScrollEvent> {
let touch_phase = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started,
UIGestureRecognizerState::Changed => TouchPhase::Moved,
UIGestureRecognizerState::Ended => TouchPhase::Ended,
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled
}
_ => return None,
};
let translation = recognizer.translationInView(Some(view));
recognizer.setTranslation_inView(CGPoint::ZERO, Some(view));
let location = recognizer.locationInView(Some(view));
Some(ScrollEvent {
position: Point::new(px(location.x as f32), px(location.y as f32)),
delta: ScrollDelta::Pixels(Point::new(
px(translation.x as f32),
px(translation.y as f32),
)),
modifiers: modifiers(recognizer.modifierFlags()),
touch_phase,
source: ScrollSource::Pointer,
})
}
fn modifiers(flags: UIKeyModifierFlags) -> Modifiers {
Modifiers {
control: flags.contains(UIKeyModifierFlags::Control),
alt: flags.contains(UIKeyModifierFlags::Alternate),
shift: flags.contains(UIKeyModifierFlags::Shift),
platform: flags.contains(UIKeyModifierFlags::Command),
function: false,
}
}
pub fn touch_force(touch: &UITouch) -> Option<f32> {
let maximum_force = touch.maximumPossibleForce();
if maximum_force <= 0.0 {
return None;
}
Some((touch.force() / maximum_force).clamp(0.0, 1.0) as f32)
}