mirror of
https://github.com/rust-windowing/winit.git
synced 2026-09-03 07:10:05 -04:00
iOS: Split classes in view.rs into separate files (#3511)
* Move application delegate to its own file * Move window subclass to window.rs * Split view controller to separate file
This commit is contained in:
103
src/platform_impl/ios/app_delegate.rs
Normal file
103
src/platform_impl/ios/app_delegate.rs
Normal file
@@ -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::<WinitUIWindow>() {
|
||||||
|
// 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::<WinitUIWindow>() {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ use objc2::{msg_send, sel};
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use super::uikit::UIView;
|
use super::uikit::UIView;
|
||||||
use super::view::WinitUIWindow;
|
use super::window::WinitUIWindow;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ use crate::{
|
|||||||
window::{CustomCursor, CustomCursorSource},
|
window::{CustomCursor, CustomCursorSource},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{app_state, monitor, view, MonitorHandle};
|
use super::app_delegate::AppDelegate;
|
||||||
|
use super::{app_state, monitor, MonitorHandle};
|
||||||
use super::{
|
use super::{
|
||||||
app_state::AppState,
|
app_state::AppState,
|
||||||
uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
|
uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
|
||||||
@@ -201,14 +202,14 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
app_state::will_launch(self.mtm, handler);
|
app_state::will_launch(self.mtm, handler);
|
||||||
|
|
||||||
// Ensure application delegate is initialized
|
// Ensure application delegate is initialized
|
||||||
view::WinitApplicationDelegate::class();
|
let _ = AppDelegate::class();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
UIApplicationMain(
|
UIApplicationMain(
|
||||||
0,
|
0,
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
None,
|
None,
|
||||||
Some(&NSString::from_str("WinitApplicationDelegate")),
|
Some(&NSString::from_str(AppDelegate::NAME)),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|||||||
@@ -58,12 +58,14 @@
|
|||||||
#![cfg(ios_platform)]
|
#![cfg(ios_platform)]
|
||||||
#![allow(clippy::let_unit_value)]
|
#![allow(clippy::let_unit_value)]
|
||||||
|
|
||||||
|
mod app_delegate;
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod ffi;
|
mod ffi;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
mod uikit;
|
mod uikit;
|
||||||
mod view;
|
mod view;
|
||||||
|
mod view_controller;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![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::rc::Id;
|
||||||
use objc2::runtime::AnyClass;
|
use objc2::runtime::AnyClass;
|
||||||
use objc2::{
|
use objc2::{
|
||||||
@@ -10,20 +10,15 @@ use objc2::{
|
|||||||
|
|
||||||
use super::app_state::{self, EventWrapper};
|
use super::app_state::{self, EventWrapper};
|
||||||
use super::uikit::{
|
use super::uikit::{
|
||||||
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIGestureRecognizerState,
|
UIEvent, UIForceTouchCapability, UIGestureRecognizerState, UIPinchGestureRecognizer,
|
||||||
UIInterfaceOrientationMask, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer,
|
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase,
|
||||||
UIStatusBarStyle, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType,
|
UITouchType, UITraitCollection, UIView,
|
||||||
UITraitCollection, UIView, UIViewController, UIWindow,
|
|
||||||
};
|
};
|
||||||
use super::window::WindowId;
|
use super::window::WinitUIWindow;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::PhysicalPosition,
|
dpi::PhysicalPosition,
|
||||||
event::{Event, Force, Touch, TouchPhase, WindowEvent},
|
event::{Event, Force, Touch, TouchPhase, WindowEvent},
|
||||||
platform::ios::ValidOrientations,
|
platform_impl::platform::DEVICE_ID,
|
||||||
platform_impl::platform::{
|
|
||||||
ffi::{UIRectEdge, UIUserInterfaceIdiom},
|
|
||||||
Fullscreen, DEVICE_ID,
|
|
||||||
},
|
|
||||||
window::{WindowAttributes, WindowId as RootWindowId},
|
window::{WindowAttributes, WindowId as RootWindowId},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -394,339 +389,3 @@ impl WinitView {
|
|||||||
app_state::handle_nonuser_events(mtm, touch_events);
|
app_state::handle_nonuser_events(mtm, touch_events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ViewControllerState {
|
|
||||||
prefers_status_bar_hidden: Cell<bool>,
|
|
||||||
preferred_status_bar_style: Cell<UIStatusBarStyle>,
|
|
||||||
prefers_home_indicator_auto_hidden: Cell<bool>,
|
|
||||||
supported_orientations: Cell<UIInterfaceOrientationMask>,
|
|
||||||
preferred_screen_edges_deferring_system_gestures: Cell<UIRectEdge>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Self> {
|
|
||||||
// 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<Self> = 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<Self> {
|
|
||||||
let this: Id<Self> = 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::<WinitUIWindow>() {
|
|
||||||
// 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::<WinitUIWindow>() {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
183
src/platform_impl/ios/view_controller.rs
Normal file
183
src/platform_impl/ios/view_controller.rs
Normal file
@@ -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<bool>,
|
||||||
|
preferred_status_bar_style: Cell<UIStatusBarStyle>,
|
||||||
|
prefers_home_indicator_auto_hidden: Cell<bool>,
|
||||||
|
supported_orientations: Cell<UIInterfaceOrientationMask>,
|
||||||
|
preferred_screen_edges_deferring_system_gestures: Cell<UIRectEdge>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Self> {
|
||||||
|
// 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<Self> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,15 @@ use std::collections::VecDeque;
|
|||||||
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
|
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::AnyObject;
|
use objc2::runtime::{AnyObject, NSObject};
|
||||||
use objc2::{class, msg_send};
|
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||||
|
|
||||||
use super::app_state::EventWrapper;
|
use super::app_state::EventWrapper;
|
||||||
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
|
use super::uikit::{
|
||||||
use super::view::{WinitUIWindow, WinitView, WinitViewController};
|
UIApplication, UIResponder, UIScreen, UIScreenOverscanCompensation, UIViewController, UIWindow,
|
||||||
|
};
|
||||||
|
use super::view::WinitView;
|
||||||
|
use super::view_controller::WinitViewController;
|
||||||
use crate::{
|
use crate::{
|
||||||
cursor::Cursor,
|
cursor::Cursor,
|
||||||
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
|
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<Self> {
|
||||||
|
let this: Id<Self> = 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 {
|
pub struct Inner {
|
||||||
window: Id<WinitUIWindow>,
|
window: Id<WinitUIWindow>,
|
||||||
view_controller: Id<WinitViewController>,
|
view_controller: Id<WinitViewController>,
|
||||||
|
|||||||
Reference in New Issue
Block a user