cranpose 0.1.150

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

use std::cell::RefCell;

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, UIWindow,
    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>() {
                SCENE.with(|cell| *cell.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] }
    }
}

thread_local! {
    static SCENE: RefCell<Option<Retained<UIWindowScene>>> = const { RefCell::new(None) };
    static WINDOW: RefCell<Option<Retained<UIWindow>>> = const { RefCell::new(None) };
}

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;
    };
    WINDOW.with(|cell| *cell.borrow_mut() = Some(ui_window));
    link();
}

fn link() {
    if MainThreadMarker::new().is_none() {
        return;
    }
    SCENE.with(|scene_cell| {
        let scene_borrow = scene_cell.borrow();
        let Some(scene) = scene_borrow.as_ref() else {
            return;
        };
        WINDOW.with(|window_cell| {
            let window_borrow = window_cell.borrow();
            let Some(window) = window_borrow.as_ref() else {
                return;
            };
            if window.windowScene().is_none() {
                window.setWindowScene(Some(scene));
                window.makeKeyAndVisible();
            }
        });
    });
}