cranpose 0.9.37

Build desktop, mobile and web apps with composable Rust functions
Documentation
#![expect(unsafe_code)]

use objc2::{
    ClassType, MainThreadMarker, MainThreadOnly, Message, define_class, msg_send, rc::Retained,
    runtime::AnyClass,
};
use objc2_foundation::{NSObject, NSObjectProtocol};
use objc2_ui_kit::{
    UIScene, UISceneConnectionOptions, UISceneDelegate, UISceneSession, UIView, UIWindowScene,
    UIWindowSceneDelegate,
};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use winit::window::Window;

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

    unsafe impl NSObjectProtocol for SceneDelegate {}

    unsafe impl UISceneDelegate for SceneDelegate {
        #[unsafe(method(scene:willConnectToSession:options:))]
        fn scene_will_connect(
            &self,
            scene: &UIScene,
            _session: &UISceneSession,
            _options: &UISceneConnectionOptions,
        ) {
            if let Some(window_scene) = scene.downcast_ref::<UIWindowScene>() {
                crate::thread_state::with(|state| {
                    *state.ios.window_scene.borrow_mut() = Some(window_scene.retain());
                });
                link();
            }
        }
    }

    unsafe impl UIWindowSceneDelegate for SceneDelegate {}
);

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

pub(crate) fn register_class() {
    let _: &AnyClass = SceneDelegate::class();
    if let Some(mtm) = MainThreadMarker::new() {
        let _ = SceneDelegate::new(mtm);
    }
}

pub(crate) fn hold_window(window: &dyn Window) {
    let Ok(handle) = window.window_handle() else {
        return;
    };
    let RawWindowHandle::UiKit(uikit) = handle.as_raw() else {
        return;
    };
    let view: *mut UIView = uikit.ui_view.as_ptr().cast();
    let ui_window = unsafe { (*view).window() };
    let Some(ui_window) = ui_window else {
        return;
    };
    crate::thread_state::with(|state| *state.ios.window.borrow_mut() = Some(ui_window));
    link();
}

fn link() {
    if MainThreadMarker::new().is_none() {
        return;
    }
    crate::thread_state::with(|state| {
        let scene_borrow = state.ios.window_scene.borrow();
        let Some(scene) = scene_borrow.as_ref() else {
            return;
        };
        let window_borrow = state.ios.window.borrow();
        let Some(window) = window_borrow.as_ref() else {
            return;
        };
        if window.windowScene().is_none() {
            window.setWindowScene(Some(scene));
            window.makeKeyAndVisible();
        }
    });
}