diff --git a/src/platform_impl/ios/app_delegate.rs b/src/platform_impl/ios/app_delegate.rs new file mode 100644 index 0000000000..eb93cdf34c --- /dev/null +++ b/src/platform_impl/ios/app_delegate.rs @@ -0,0 +1,103 @@ +use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; + +use super::app_state::{self, EventWrapper}; +use super::uikit::{UIApplication, UIWindow}; +use super::window::WinitUIWindow; +use crate::{ + event::{Event, WindowEvent}, + window::WindowId as RootWindowId, +}; + +declare_class!( + pub struct AppDelegate; + + unsafe impl ClassType for AppDelegate { + type Super = NSObject; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "WinitApplicationDelegate"; + } + + impl DeclaredClass for AppDelegate {} + + // UIApplicationDelegate protocol + unsafe impl AppDelegate { + #[method(application:didFinishLaunchingWithOptions:)] + fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool { + app_state::did_finish_launching(MainThreadMarker::new().unwrap()); + true + } + + #[method(applicationDidBecomeActive:)] + fn did_become_active(&self, _application: &UIApplication) { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed)) + } + + #[method(applicationWillResignActive:)] + fn will_resign_active(&self, _application: &UIApplication) { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended)) + } + + #[method(applicationWillEnterForeground:)] + fn will_enter_foreground(&self, application: &UIApplication) { + self.send_occluded_event_for_all_windows(application, false); + } + + #[method(applicationDidEnterBackground:)] + fn did_enter_background(&self, application: &UIApplication) { + self.send_occluded_event_for_all_windows(application, true); + } + + #[method(applicationWillTerminate:)] + fn will_terminate(&self, application: &UIApplication) { + let mut events = Vec::new(); + for window in application.windows().iter() { + if window.is_kind_of::() { + // SAFETY: We just checked that the window is a `winit` window + let window = unsafe { + let ptr: *const UIWindow = window; + let ptr: *const WinitUIWindow = ptr.cast(); + &*ptr + }; + events.push(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(window.id()), + event: WindowEvent::Destroyed, + })); + } + } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_events(mtm, events); + app_state::terminated(mtm); + } + + #[method(applicationDidReceiveMemoryWarning:)] + fn did_receive_memory_warning(&self, _application: &UIApplication) { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning)) + } + } +); + +impl AppDelegate { + fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) { + let mut events = Vec::new(); + for window in application.windows().iter() { + if window.is_kind_of::() { + // SAFETY: We just checked that the window is a `winit` window + let window = unsafe { + let ptr: *const UIWindow = window; + let ptr: *const WinitUIWindow = ptr.cast(); + &*ptr + }; + events.push(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(window.id()), + event: WindowEvent::Occluded(occluded), + })); + } + } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_events(mtm, events); + } +} diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 49492617b2..d0105dd8a8 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -25,7 +25,7 @@ use objc2::{msg_send, sel}; use once_cell::sync::Lazy; use super::uikit::UIView; -use super::view::WinitUIWindow; +use super::window::WinitUIWindow; use crate::{ dpi::PhysicalSize, event::{Event, InnerSizeWriter, StartCause, WindowEvent}, diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index da3a918104..a0ef0e0f55 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -27,7 +27,8 @@ use crate::{ window::{CustomCursor, CustomCursorSource}, }; -use super::{app_state, monitor, view, MonitorHandle}; +use super::app_delegate::AppDelegate; +use super::{app_state, monitor, MonitorHandle}; use super::{ app_state::AppState, uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen}, @@ -201,14 +202,14 @@ impl EventLoop { app_state::will_launch(self.mtm, handler); // Ensure application delegate is initialized - view::WinitApplicationDelegate::class(); + let _ = AppDelegate::class(); unsafe { UIApplicationMain( 0, ptr::null(), None, - Some(&NSString::from_str("WinitApplicationDelegate")), + Some(&NSString::from_str(AppDelegate::NAME)), ) }; unreachable!() diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs index 1a38df0e33..496ad35967 100644 --- a/src/platform_impl/ios/mod.rs +++ b/src/platform_impl/ios/mod.rs @@ -58,12 +58,14 @@ #![cfg(ios_platform)] #![allow(clippy::let_unit_value)] +mod app_delegate; mod app_state; mod event_loop; mod ffi; mod monitor; mod uikit; mod view; +mod view_controller; mod window; use std::fmt; diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index 0f6a677799..3f3fa8b6cb 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -1,7 +1,7 @@ #![allow(clippy::unnecessary_cast)] -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; -use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet}; +use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSSet}; use objc2::rc::Id; use objc2::runtime::AnyClass; use objc2::{ @@ -10,20 +10,15 @@ use objc2::{ use super::app_state::{self, EventWrapper}; use super::uikit::{ - UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIGestureRecognizerState, - UIInterfaceOrientationMask, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, - UIStatusBarStyle, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType, - UITraitCollection, UIView, UIViewController, UIWindow, + UIEvent, UIForceTouchCapability, UIGestureRecognizerState, UIPinchGestureRecognizer, + UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase, + UITouchType, UITraitCollection, UIView, }; -use super::window::WindowId; +use super::window::WinitUIWindow; use crate::{ dpi::PhysicalPosition, event::{Event, Force, Touch, TouchPhase, WindowEvent}, - platform::ios::ValidOrientations, - platform_impl::platform::{ - ffi::{UIRectEdge, UIUserInterfaceIdiom}, - Fullscreen, DEVICE_ID, - }, + platform_impl::platform::DEVICE_ID, window::{WindowAttributes, WindowId as RootWindowId}, }; @@ -394,339 +389,3 @@ impl WinitView { app_state::handle_nonuser_events(mtm, touch_events); } } - -pub struct ViewControllerState { - prefers_status_bar_hidden: Cell, - preferred_status_bar_style: Cell, - prefers_home_indicator_auto_hidden: Cell, - supported_orientations: Cell, - preferred_screen_edges_deferring_system_gestures: Cell, -} - -declare_class!( - pub(crate) struct WinitViewController; - - unsafe impl ClassType for WinitViewController { - #[inherits(UIResponder, NSObject)] - type Super = UIViewController; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "WinitUIViewController"; - } - - impl DeclaredClass for WinitViewController { - type Ivars = ViewControllerState; - } - - unsafe impl WinitViewController { - #[method(shouldAutorotate)] - fn should_autorotate(&self) -> bool { - true - } - - #[method(prefersStatusBarHidden)] - fn prefers_status_bar_hidden(&self) -> bool { - self.ivars().prefers_status_bar_hidden.get() - } - - #[method(preferredStatusBarStyle)] - fn preferred_status_bar_style(&self) -> UIStatusBarStyle { - self.ivars().preferred_status_bar_style.get() - } - - #[method(prefersHomeIndicatorAutoHidden)] - fn prefers_home_indicator_auto_hidden(&self) -> bool { - self.ivars().prefers_home_indicator_auto_hidden.get() - } - - #[method(supportedInterfaceOrientations)] - fn supported_orientations(&self) -> UIInterfaceOrientationMask { - self.ivars().supported_orientations.get() - } - - #[method(preferredScreenEdgesDeferringSystemGestures)] - fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge { - self.ivars() - .preferred_screen_edges_deferring_system_gestures - .get() - } - } -); - -impl WinitViewController { - pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) { - self.ivars().prefers_status_bar_hidden.set(val); - self.setNeedsStatusBarAppearanceUpdate(); - } - - pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) { - self.ivars().preferred_status_bar_style.set(val); - self.setNeedsStatusBarAppearanceUpdate(); - } - - pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) { - self.ivars().prefers_home_indicator_auto_hidden.set(val); - let os_capabilities = app_state::os_capabilities(); - if os_capabilities.home_indicator_hidden { - self.setNeedsUpdateOfHomeIndicatorAutoHidden(); - } else { - os_capabilities.home_indicator_hidden_err_msg("ignoring") - } - } - - pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) { - self.ivars() - .preferred_screen_edges_deferring_system_gestures - .set(val); - let os_capabilities = app_state::os_capabilities(); - if os_capabilities.defer_system_gestures { - self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures(); - } else { - os_capabilities.defer_system_gestures_err_msg("ignoring") - } - } - - pub(crate) fn set_supported_interface_orientations( - &self, - mtm: MainThreadMarker, - valid_orientations: ValidOrientations, - ) { - let mask = match ( - valid_orientations, - UIDevice::current(mtm).userInterfaceIdiom(), - ) { - (ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => { - UIInterfaceOrientationMask::AllButUpsideDown - } - (ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All, - (ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape, - (ValidOrientations::Portrait, UIUserInterfaceIdiom::Phone) => { - UIInterfaceOrientationMask::Portrait - } - (ValidOrientations::Portrait, _) => { - UIInterfaceOrientationMask::Portrait - | UIInterfaceOrientationMask::PortraitUpsideDown - } - }; - self.ivars().supported_orientations.set(mask); - UIViewController::attemptRotationToDeviceOrientation(); - } - - pub(crate) fn new( - mtm: MainThreadMarker, - window_attributes: &WindowAttributes, - view: &UIView, - ) -> Id { - // These are set properly below, we just to set them to something in the meantime. - let this = Self::alloc().set_ivars(ViewControllerState { - prefers_status_bar_hidden: Cell::new(false), - preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default), - prefers_home_indicator_auto_hidden: Cell::new(false), - supported_orientations: Cell::new(UIInterfaceOrientationMask::All), - preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE), - }); - let this: Id = unsafe { msg_send_id![super(this), init] }; - - this.set_prefers_status_bar_hidden( - window_attributes - .platform_specific - .prefers_status_bar_hidden, - ); - - this.set_preferred_status_bar_style( - window_attributes - .platform_specific - .preferred_status_bar_style - .into(), - ); - - this.set_supported_interface_orientations( - mtm, - window_attributes.platform_specific.valid_orientations, - ); - - this.set_prefers_home_indicator_auto_hidden( - window_attributes - .platform_specific - .prefers_home_indicator_hidden, - ); - - this.set_preferred_screen_edges_deferring_system_gestures( - window_attributes - .platform_specific - .preferred_screen_edges_deferring_system_gestures - .into(), - ); - - this.setView(Some(view)); - - this - } -} - -declare_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct WinitUIWindow; - - unsafe impl ClassType for WinitUIWindow { - #[inherits(UIResponder, NSObject)] - type Super = UIWindow; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "WinitUIWindow"; - } - - impl DeclaredClass for WinitUIWindow {} - - unsafe impl WinitUIWindow { - #[method(becomeKeyWindow)] - fn become_key_window(&self) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event( - mtm, - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(self.id()), - event: WindowEvent::Focused(true), - }), - ); - let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; - } - - #[method(resignKeyWindow)] - fn resign_key_window(&self) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event( - mtm, - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(self.id()), - event: WindowEvent::Focused(false), - }), - ); - let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; - } - } -); - -impl WinitUIWindow { - pub(crate) fn new( - mtm: MainThreadMarker, - window_attributes: &WindowAttributes, - frame: CGRect, - view_controller: &UIViewController, - ) -> Id { - let this: Id = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] }; - - this.setRootViewController(Some(view_controller)); - - match window_attributes.fullscreen.clone().map(Into::into) { - Some(Fullscreen::Exclusive(ref video_mode)) => { - let monitor = video_mode.monitor(); - let screen = monitor.ui_screen(mtm); - screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); - this.setScreen(screen); - } - Some(Fullscreen::Borderless(Some(ref monitor))) => { - let screen = monitor.ui_screen(mtm); - this.setScreen(screen); - } - _ => (), - } - - this - } - - pub(crate) fn id(&self) -> WindowId { - (self as *const Self as usize as u64).into() - } -} - -declare_class!( - pub struct WinitApplicationDelegate; - - unsafe impl ClassType for WinitApplicationDelegate { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "WinitApplicationDelegate"; - } - - impl DeclaredClass for WinitApplicationDelegate {} - - // UIApplicationDelegate protocol - unsafe impl WinitApplicationDelegate { - #[method(application:didFinishLaunchingWithOptions:)] - fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool { - app_state::did_finish_launching(MainThreadMarker::new().unwrap()); - true - } - - #[method(applicationDidBecomeActive:)] - fn did_become_active(&self, _application: &UIApplication) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed)) - } - - #[method(applicationWillResignActive:)] - fn will_resign_active(&self, _application: &UIApplication) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended)) - } - - #[method(applicationWillEnterForeground:)] - fn will_enter_foreground(&self, application: &UIApplication) { - self.send_occluded_event_for_all_windows(application, false); - } - - #[method(applicationDidEnterBackground:)] - fn did_enter_background(&self, application: &UIApplication) { - self.send_occluded_event_for_all_windows(application, true); - } - - #[method(applicationWillTerminate:)] - fn will_terminate(&self, application: &UIApplication) { - let mut events = Vec::new(); - for window in application.windows().iter() { - if window.is_kind_of::() { - // SAFETY: We just checked that the window is a `winit` window - let window = unsafe { - let ptr: *const UIWindow = window; - let ptr: *const WinitUIWindow = ptr.cast(); - &*ptr - }; - events.push(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::Destroyed, - })); - } - } - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_events(mtm, events); - app_state::terminated(mtm); - } - - #[method(applicationDidReceiveMemoryWarning:)] - fn did_receive_memory_warning(&self, _application: &UIApplication) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning)) - } - } -); - -impl WinitApplicationDelegate { - fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) { - let mut events = Vec::new(); - for window in application.windows().iter() { - if window.is_kind_of::() { - // SAFETY: We just checked that the window is a `winit` window - let window = unsafe { - let ptr: *const UIWindow = window; - let ptr: *const WinitUIWindow = ptr.cast(); - &*ptr - }; - events.push(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::Occluded(occluded), - })); - } - } - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_events(mtm, events); - } -} diff --git a/src/platform_impl/ios/view_controller.rs b/src/platform_impl/ios/view_controller.rs new file mode 100644 index 0000000000..5a324a498e --- /dev/null +++ b/src/platform_impl/ios/view_controller.rs @@ -0,0 +1,183 @@ +use std::cell::Cell; + +use icrate::Foundation::{MainThreadMarker, NSObject}; +use objc2::rc::Id; +use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; + +use super::app_state::{self}; +use super::uikit::{ + UIDevice, UIInterfaceOrientationMask, UIResponder, UIStatusBarStyle, UIView, UIViewController, +}; +use crate::{ + platform::ios::ValidOrientations, + platform_impl::platform::ffi::{UIRectEdge, UIUserInterfaceIdiom}, + window::WindowAttributes, +}; + +pub struct ViewControllerState { + prefers_status_bar_hidden: Cell, + preferred_status_bar_style: Cell, + prefers_home_indicator_auto_hidden: Cell, + supported_orientations: Cell, + preferred_screen_edges_deferring_system_gestures: Cell, +} + +declare_class!( + pub(crate) struct WinitViewController; + + unsafe impl ClassType for WinitViewController { + #[inherits(UIResponder, NSObject)] + type Super = UIViewController; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "WinitUIViewController"; + } + + impl DeclaredClass for WinitViewController { + type Ivars = ViewControllerState; + } + + unsafe impl WinitViewController { + #[method(shouldAutorotate)] + fn should_autorotate(&self) -> bool { + true + } + + #[method(prefersStatusBarHidden)] + fn prefers_status_bar_hidden(&self) -> bool { + self.ivars().prefers_status_bar_hidden.get() + } + + #[method(preferredStatusBarStyle)] + fn preferred_status_bar_style(&self) -> UIStatusBarStyle { + self.ivars().preferred_status_bar_style.get() + } + + #[method(prefersHomeIndicatorAutoHidden)] + fn prefers_home_indicator_auto_hidden(&self) -> bool { + self.ivars().prefers_home_indicator_auto_hidden.get() + } + + #[method(supportedInterfaceOrientations)] + fn supported_orientations(&self) -> UIInterfaceOrientationMask { + self.ivars().supported_orientations.get() + } + + #[method(preferredScreenEdgesDeferringSystemGestures)] + fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge { + self.ivars() + .preferred_screen_edges_deferring_system_gestures + .get() + } + } +); + +impl WinitViewController { + pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) { + self.ivars().prefers_status_bar_hidden.set(val); + self.setNeedsStatusBarAppearanceUpdate(); + } + + pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) { + self.ivars().preferred_status_bar_style.set(val); + self.setNeedsStatusBarAppearanceUpdate(); + } + + pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) { + self.ivars().prefers_home_indicator_auto_hidden.set(val); + let os_capabilities = app_state::os_capabilities(); + if os_capabilities.home_indicator_hidden { + self.setNeedsUpdateOfHomeIndicatorAutoHidden(); + } else { + os_capabilities.home_indicator_hidden_err_msg("ignoring") + } + } + + pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) { + self.ivars() + .preferred_screen_edges_deferring_system_gestures + .set(val); + let os_capabilities = app_state::os_capabilities(); + if os_capabilities.defer_system_gestures { + self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures(); + } else { + os_capabilities.defer_system_gestures_err_msg("ignoring") + } + } + + pub(crate) fn set_supported_interface_orientations( + &self, + mtm: MainThreadMarker, + valid_orientations: ValidOrientations, + ) { + let mask = match ( + valid_orientations, + UIDevice::current(mtm).userInterfaceIdiom(), + ) { + (ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => { + UIInterfaceOrientationMask::AllButUpsideDown + } + (ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All, + (ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape, + (ValidOrientations::Portrait, UIUserInterfaceIdiom::Phone) => { + UIInterfaceOrientationMask::Portrait + } + (ValidOrientations::Portrait, _) => { + UIInterfaceOrientationMask::Portrait + | UIInterfaceOrientationMask::PortraitUpsideDown + } + }; + self.ivars().supported_orientations.set(mask); + UIViewController::attemptRotationToDeviceOrientation(); + } + + pub(crate) fn new( + mtm: MainThreadMarker, + window_attributes: &WindowAttributes, + view: &UIView, + ) -> Id { + // These are set properly below, we just to set them to something in the meantime. + let this = Self::alloc().set_ivars(ViewControllerState { + prefers_status_bar_hidden: Cell::new(false), + preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default), + prefers_home_indicator_auto_hidden: Cell::new(false), + supported_orientations: Cell::new(UIInterfaceOrientationMask::All), + preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE), + }); + let this: Id = unsafe { msg_send_id![super(this), init] }; + + this.set_prefers_status_bar_hidden( + window_attributes + .platform_specific + .prefers_status_bar_hidden, + ); + + this.set_preferred_status_bar_style( + window_attributes + .platform_specific + .preferred_status_bar_style + .into(), + ); + + this.set_supported_interface_orientations( + mtm, + window_attributes.platform_specific.valid_orientations, + ); + + this.set_prefers_home_indicator_auto_hidden( + window_attributes + .platform_specific + .prefers_home_indicator_hidden, + ); + + this.set_preferred_screen_edges_deferring_system_gestures( + window_attributes + .platform_specific + .preferred_screen_edges_deferring_system_gestures + .into(), + ); + + this.setView(Some(view)); + + this + } +} diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index d37b10cdfc..81e88e5635 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -5,12 +5,15 @@ use std::collections::VecDeque; use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker}; use log::{debug, warn}; use objc2::rc::Id; -use objc2::runtime::AnyObject; -use objc2::{class, msg_send}; +use objc2::runtime::{AnyObject, NSObject}; +use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass}; use super::app_state::EventWrapper; -use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation}; -use super::view::{WinitUIWindow, WinitView, WinitViewController}; +use super::uikit::{ + UIApplication, UIResponder, UIScreen, UIScreenOverscanCompensation, UIViewController, UIWindow, +}; +use super::view::WinitView; +use super::view_controller::WinitViewController; use crate::{ cursor::Cursor, dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, @@ -25,6 +28,81 @@ use crate::{ }, }; +declare_class!( + #[derive(Debug, PartialEq, Eq, Hash)] + pub(crate) struct WinitUIWindow; + + unsafe impl ClassType for WinitUIWindow { + #[inherits(UIResponder, NSObject)] + type Super = UIWindow; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "WinitUIWindow"; + } + + impl DeclaredClass for WinitUIWindow {} + + unsafe impl WinitUIWindow { + #[method(becomeKeyWindow)] + fn become_key_window(&self) { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event( + mtm, + EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(self.id()), + event: WindowEvent::Focused(true), + }), + ); + let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; + } + + #[method(resignKeyWindow)] + fn resign_key_window(&self) { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event( + mtm, + EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(self.id()), + event: WindowEvent::Focused(false), + }), + ); + let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; + } + } +); + +impl WinitUIWindow { + pub(crate) fn new( + mtm: MainThreadMarker, + window_attributes: &WindowAttributes, + frame: CGRect, + view_controller: &UIViewController, + ) -> Id { + let this: Id = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] }; + + this.setRootViewController(Some(view_controller)); + + match window_attributes.fullscreen.clone().map(Into::into) { + Some(Fullscreen::Exclusive(ref video_mode)) => { + let monitor = video_mode.monitor(); + let screen = monitor.ui_screen(mtm); + screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + this.setScreen(screen); + } + Some(Fullscreen::Borderless(Some(ref monitor))) => { + let screen = monitor.ui_screen(mtm); + this.setScreen(screen); + } + _ => (), + } + + this + } + + pub(crate) fn id(&self) -> WindowId { + (self as *const Self as usize as u64).into() + } +} + pub struct Inner { window: Id, view_controller: Id,