cranpose 0.1.155

Cranpose runtime and UI facade
Documentation
#![allow(unsafe_code)]

use std::cell::RefCell;

use cranpose_services::push_back_request;
use objc2::{MainThreadMarker, MainThreadOnly, define_class, msg_send, rc::Retained, sel};
use objc2_foundation::{NSObject, NSObjectProtocol};
use objc2_ui_kit::{UIGestureRecognizerState, UIRectEdge, UIScreenEdgePanGestureRecognizer};

define_class!(
    #[unsafe(super(NSObject))]
    #[thread_kind = MainThreadOnly]
    #[name = "CranposeBackGestureTarget"]
    #[ivars = ()]
    struct BackGestureTarget;

    unsafe impl NSObjectProtocol for BackGestureTarget {}

    impl BackGestureTarget {
        #[unsafe(method(handleEdgePan:))]
        fn handle_edge_pan(&self, recognizer: &UIScreenEdgePanGestureRecognizer) {
            if recognizer.state() == UIGestureRecognizerState::Ended {
                push_back_request();
            }
        }
    }
);

impl BackGestureTarget {
    fn new(mtm: MainThreadMarker) -> Retained<Self> {
        let this = Self::alloc(mtm).set_ivars(());
        unsafe { msg_send![super(this), init] }
    }
}

thread_local! {
    static TARGET: RefCell<Option<Retained<BackGestureTarget>>> = const { RefCell::new(None) };
}

pub(crate) fn register() {
    let Some(mtm) = MainThreadMarker::new() else {
        return;
    };
    let Some(root) = crate::ios_file_picker::root_view_controller(mtm) else {
        return;
    };
    let Some(root_view) = root.view() else {
        return;
    };
    let target = BackGestureTarget::new(mtm);
    let recognizer = unsafe {
        UIScreenEdgePanGestureRecognizer::initWithTarget_action(
            UIScreenEdgePanGestureRecognizer::alloc(mtm),
            Some(&*target),
            Some(sel!(handleEdgePan:)),
        )
    };
    recognizer.setEdges(UIRectEdge::Left);
    root_view.addGestureRecognizer(&recognizer);
    TARGET.with(|cell| *cell.borrow_mut() = Some(target));
}