2019-08-14 21:57:16 +10:00
|
|
|
use raw_window_handle::{ios::IOSHandle, RawWindowHandle};
|
2019-05-26 11:10:41 +10:00
|
|
|
use std::{
|
|
|
|
collections::VecDeque,
|
|
|
|
ops::{Deref, DerefMut},
|
|
|
|
};
|
|
|
|
|
2019-09-05 07:23:11 +10:00
|
|
|
use objc::runtime::{Class, Object, BOOL, NO, YES};
|
2019-06-22 01:33:15 +10:00
|
|
|
|
|
|
|
use crate::{
|
2019-10-19 02:31:26 +11:00
|
|
|
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
|
2019-06-22 01:33:15 +10:00
|
|
|
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
2019-09-17 04:27:46 +10:00
|
|
|
event::{Event, WindowEvent},
|
2019-06-22 01:33:15 +10:00
|
|
|
icon::Icon,
|
|
|
|
monitor::MonitorHandle as RootMonitorHandle,
|
2019-07-31 16:57:31 +10:00
|
|
|
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
|
2019-06-22 01:33:15 +10:00
|
|
|
platform_impl::platform::{
|
2019-10-19 02:31:26 +11:00
|
|
|
app_state,
|
|
|
|
event_loop::{self, EventProxy, EventWrapper},
|
2019-07-31 16:57:31 +10:00
|
|
|
ffi::{
|
|
|
|
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
|
2019-08-07 06:47:00 +10:00
|
|
|
UIRectEdge, UIScreenOverscanCompensation,
|
2019-07-31 16:57:31 +10:00
|
|
|
},
|
2019-06-22 01:33:15 +10:00
|
|
|
monitor, view, EventLoopWindowTarget, MonitorHandle,
|
2019-05-26 11:10:41 +10:00
|
|
|
},
|
2019-09-17 04:27:46 +10:00
|
|
|
window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId},
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
pub struct Inner {
|
|
|
|
pub window: id,
|
|
|
|
pub view_controller: id,
|
|
|
|
pub view: id,
|
2019-09-05 07:23:11 +10:00
|
|
|
gl_or_metal_backed: bool,
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Inner {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
let () = msg_send![self.view, release];
|
|
|
|
let () = msg_send![self.view_controller, release];
|
|
|
|
let () = msg_send![self.window, release];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Inner {
|
|
|
|
pub fn set_title(&self, _title: &str) {
|
|
|
|
debug!("`Window::set_title` is ignored on iOS")
|
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn set_visible(&self, visible: bool) {
|
|
|
|
match visible {
|
|
|
|
true => unsafe {
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![self.window, setHidden: NO];
|
2019-05-30 11:29:54 +10:00
|
|
|
},
|
|
|
|
false => unsafe {
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![self.window, setHidden: YES];
|
2019-05-30 11:29:54 +10:00
|
|
|
},
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn request_redraw(&self) {
|
|
|
|
unsafe {
|
2019-09-05 07:23:11 +10:00
|
|
|
if self.gl_or_metal_backed {
|
|
|
|
// `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer.
|
|
|
|
// Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using
|
|
|
|
// raw or gl/metal for drawing this work is completely avoided.
|
|
|
|
//
|
|
|
|
// The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via
|
|
|
|
// testing.
|
|
|
|
//
|
|
|
|
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
|
|
|
|
app_state::queue_gl_or_metal_redraw(self.window);
|
|
|
|
} else {
|
|
|
|
let () = msg_send![self.view, setNeedsDisplay];
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
2019-05-30 11:29:54 +10:00
|
|
|
|
2020-01-04 17:33:07 +11:00
|
|
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
|
|
|
let safe_area = self.safe_area_screen_space();
|
2019-10-19 02:31:26 +11:00
|
|
|
let position = LogicalPosition {
|
2020-01-04 17:33:07 +11:00
|
|
|
x: safe_area.origin.x as f64,
|
|
|
|
y: safe_area.origin.y as f64,
|
2019-10-19 02:31:26 +11:00
|
|
|
};
|
2020-01-04 06:52:27 +11:00
|
|
|
let dpi_factor = self.scale_factor();
|
2019-10-19 02:31:26 +11:00
|
|
|
Ok(position.to_physical(dpi_factor))
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 17:33:07 +11:00
|
|
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
|
|
|
let screen_frame = self.screen_frame();
|
2019-10-19 02:31:26 +11:00
|
|
|
let position = LogicalPosition {
|
2020-01-04 17:33:07 +11:00
|
|
|
x: screen_frame.origin.x as f64,
|
|
|
|
y: screen_frame.origin.y as f64,
|
2019-10-19 02:31:26 +11:00
|
|
|
};
|
2020-01-04 06:52:27 +11:00
|
|
|
let dpi_factor = self.scale_factor();
|
2019-10-19 02:31:26 +11:00
|
|
|
Ok(position.to_physical(dpi_factor))
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
pub fn set_outer_position(&self, physical_position: Position) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2020-01-04 06:52:27 +11:00
|
|
|
let dpi_factor = self.scale_factor();
|
2020-01-04 17:33:07 +11:00
|
|
|
let position = physical_position.to_logical::<f64>(dpi_factor);
|
2019-05-26 11:10:41 +10:00
|
|
|
let screen_frame = self.screen_frame();
|
|
|
|
let new_screen_frame = CGRect {
|
|
|
|
origin: CGPoint {
|
|
|
|
x: position.x as _,
|
|
|
|
y: position.y as _,
|
|
|
|
},
|
|
|
|
size: screen_frame.size,
|
|
|
|
};
|
|
|
|
let bounds = self.from_screen_space(new_screen_frame);
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![self.window, setBounds: bounds];
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 17:33:07 +11:00
|
|
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2020-01-04 06:52:27 +11:00
|
|
|
let dpi_factor = self.scale_factor();
|
2019-05-26 11:10:41 +10:00
|
|
|
let safe_area = self.safe_area_screen_space();
|
2019-10-19 02:31:26 +11:00
|
|
|
let size = LogicalSize {
|
2020-01-04 17:33:07 +11:00
|
|
|
width: safe_area.size.width as f64,
|
|
|
|
height: safe_area.size.height as f64,
|
2019-10-19 02:31:26 +11:00
|
|
|
};
|
|
|
|
size.to_physical(dpi_factor)
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 17:33:07 +11:00
|
|
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2020-01-04 06:52:27 +11:00
|
|
|
let dpi_factor = self.scale_factor();
|
2019-05-26 11:10:41 +10:00
|
|
|
let screen_frame = self.screen_frame();
|
2019-10-19 02:31:26 +11:00
|
|
|
let size = LogicalSize {
|
2020-01-04 17:33:07 +11:00
|
|
|
width: screen_frame.size.width as f64,
|
|
|
|
height: screen_frame.size.height as f64,
|
2019-10-19 02:31:26 +11:00
|
|
|
};
|
|
|
|
size.to_physical(dpi_factor)
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
pub fn set_inner_size(&self, _size: Size) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unimplemented!("not clear what `Window::set_inner_size` means on iOS");
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
|
2019-05-30 11:29:54 +10:00
|
|
|
warn!("`Window::set_min_inner_size` is ignored on iOS")
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
pub fn set_max_inner_size(&self, _dimensions: Option<Size>) {
|
2019-05-30 11:29:54 +10:00
|
|
|
warn!("`Window::set_max_inner_size` is ignored on iOS")
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_resizable(&self, _resizable: bool) {
|
|
|
|
warn!("`Window::set_resizable` is ignored on iOS")
|
|
|
|
}
|
|
|
|
|
2020-01-04 06:52:27 +11:00
|
|
|
pub fn scale_factor(&self) -> f64 {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
|
|
|
let hidpi: CGFloat = msg_send![self.view, contentScaleFactor];
|
|
|
|
hidpi as _
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn set_cursor_icon(&self, _cursor: CursorIcon) {
|
|
|
|
debug!("`Window::set_cursor_icon` ignored on iOS")
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
|
2019-05-30 11:29:54 +10:00
|
|
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> {
|
|
|
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn set_cursor_visible(&self, _visible: bool) {
|
|
|
|
debug!("`Window::set_cursor_visible` is ignored on iOS")
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:04:11 +11:00
|
|
|
pub fn set_minimized(&self, _minimized: bool) {
|
|
|
|
warn!("`Window::set_minimized` is ignored on iOS")
|
|
|
|
}
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
pub fn set_maximized(&self, _maximized: bool) {
|
|
|
|
warn!("`Window::set_maximized` is ignored on iOS")
|
|
|
|
}
|
|
|
|
|
2019-07-30 04:16:14 +10:00
|
|
|
pub fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-07-31 16:57:31 +10:00
|
|
|
let uiscreen = match monitor {
|
|
|
|
Some(Fullscreen::Exclusive(video_mode)) => {
|
|
|
|
let uiscreen = video_mode.video_mode.monitor.ui_screen() as id;
|
|
|
|
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode];
|
|
|
|
uiscreen
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-07-31 16:57:31 +10:00
|
|
|
Some(Fullscreen::Borderless(monitor)) => monitor.ui_screen() as id,
|
|
|
|
None => {
|
|
|
|
warn!("`Window::set_fullscreen(None)` ignored on iOS");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// this is pretty slow on iOS, so avoid doing it if we can
|
2019-08-07 06:47:00 +10:00
|
|
|
let current: id = msg_send![self.window, screen];
|
2019-07-31 16:57:31 +10:00
|
|
|
if uiscreen != current {
|
|
|
|
let () = msg_send![self.window, setScreen: uiscreen];
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
2019-08-07 06:47:00 +10:00
|
|
|
|
|
|
|
let bounds: CGRect = msg_send![uiscreen, bounds];
|
2019-07-31 16:57:31 +10:00
|
|
|
let () = msg_send![self.window, setFrame: bounds];
|
2019-08-07 06:47:00 +10:00
|
|
|
|
|
|
|
// For external displays, we must disable overscan compensation or
|
|
|
|
// the displayed image will have giant black bars surrounding it on
|
|
|
|
// each side
|
|
|
|
let () = msg_send![
|
|
|
|
uiscreen,
|
|
|
|
setOverscanCompensation: UIScreenOverscanCompensation::None
|
|
|
|
];
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 04:16:14 +10:00
|
|
|
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-05-30 11:29:54 +10:00
|
|
|
let monitor = self.current_monitor();
|
|
|
|
let uiscreen = monitor.inner.ui_screen();
|
2019-05-26 11:10:41 +10:00
|
|
|
let screen_space_bounds = self.screen_frame();
|
|
|
|
let screen_bounds: CGRect = msg_send![uiscreen, bounds];
|
|
|
|
|
|
|
|
// TODO: track fullscreen instead of relying on brittle float comparisons
|
|
|
|
if screen_space_bounds.origin.x == screen_bounds.origin.x
|
|
|
|
&& screen_space_bounds.origin.y == screen_bounds.origin.y
|
|
|
|
&& screen_space_bounds.size.width == screen_bounds.size.width
|
|
|
|
&& screen_space_bounds.size.height == screen_bounds.size.height
|
|
|
|
{
|
2019-07-30 04:16:14 +10:00
|
|
|
Some(Fullscreen::Borderless(monitor))
|
2019-05-26 11:10:41 +10:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 09:10:54 +10:00
|
|
|
pub fn set_decorations(&self, _decorations: bool) {
|
|
|
|
warn!("`Window::set_decorations` is ignored on iOS")
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_always_on_top(&self, _always_on_top: bool) {
|
|
|
|
warn!("`Window::set_always_on_top` is ignored on iOS")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_window_icon(&self, _icon: Option<Icon>) {
|
|
|
|
warn!("`Window::set_window_icon` is ignored on iOS")
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
pub fn set_ime_position(&self, _position: Position) {
|
2019-05-30 11:29:54 +10:00
|
|
|
warn!("`Window::set_ime_position` is ignored on iOS")
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn current_monitor(&self) -> RootMonitorHandle {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
|
|
|
let uiscreen: id = msg_send![self.window, screen];
|
2019-06-22 01:33:15 +10:00
|
|
|
RootMonitorHandle {
|
|
|
|
inner: MonitorHandle::retained_new(uiscreen),
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
2019-06-22 01:33:15 +10:00
|
|
|
unsafe { monitor::uiscreens() }
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
pub fn primary_monitor(&self) -> MonitorHandle {
|
2019-06-22 01:33:15 +10:00
|
|
|
unsafe { monitor::main_uiscreen() }
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn id(&self) -> WindowId {
|
|
|
|
self.window.into()
|
|
|
|
}
|
2019-08-14 21:57:16 +10:00
|
|
|
|
|
|
|
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
|
|
|
let handle = IOSHandle {
|
|
|
|
ui_window: self.window as _,
|
|
|
|
ui_view: self.view as _,
|
|
|
|
ui_view_controller: self.view_controller as _,
|
|
|
|
..IOSHandle::empty()
|
|
|
|
};
|
|
|
|
RawWindowHandle::IOS(handle)
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Window {
|
|
|
|
pub inner: Inner,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Window {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
assert_main_thread!("`Window::drop` can only be run on the main thread on iOS");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for Window {}
|
|
|
|
unsafe impl Sync for Window {}
|
|
|
|
|
|
|
|
impl Deref for Window {
|
|
|
|
type Target = Inner;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Inner {
|
|
|
|
unsafe {
|
|
|
|
assert_main_thread!("`Window` methods can only be run on the main thread on iOS");
|
|
|
|
}
|
|
|
|
&self.inner
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for Window {
|
|
|
|
fn deref_mut(&mut self) -> &mut Inner {
|
|
|
|
unsafe {
|
|
|
|
assert_main_thread!("`Window` methods can only be run on the main thread on iOS");
|
|
|
|
}
|
|
|
|
&mut self.inner
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Window {
|
|
|
|
pub fn new<T>(
|
2019-08-27 08:47:23 +10:00
|
|
|
_event_loop: &EventLoopWindowTarget<T>,
|
2019-05-26 11:10:41 +10:00
|
|
|
window_attributes: WindowAttributes,
|
|
|
|
platform_attributes: PlatformSpecificWindowBuilderAttributes,
|
2019-05-30 11:29:54 +10:00
|
|
|
) -> Result<Window, RootOsError> {
|
|
|
|
if let Some(_) = window_attributes.min_inner_size {
|
|
|
|
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
2019-05-30 11:29:54 +10:00
|
|
|
if let Some(_) = window_attributes.max_inner_size {
|
|
|
|
warn!("`WindowAttributes::max_inner_size` is ignored on iOS");
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
if window_attributes.always_on_top {
|
|
|
|
warn!("`WindowAttributes::always_on_top` is unsupported on iOS");
|
|
|
|
}
|
|
|
|
// TODO: transparency, visible
|
|
|
|
|
|
|
|
unsafe {
|
2019-07-30 04:16:14 +10:00
|
|
|
let screen = match window_attributes.fullscreen {
|
2019-07-31 16:57:31 +10:00
|
|
|
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
|
|
|
video_mode.video_mode.monitor.ui_screen() as id
|
|
|
|
}
|
2019-07-30 04:16:14 +10:00
|
|
|
Some(Fullscreen::Borderless(ref monitor)) => monitor.ui_screen() as id,
|
|
|
|
None => monitor::main_uiscreen().ui_screen(),
|
|
|
|
};
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
let screen_bounds: CGRect = msg_send![screen, bounds];
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
let frame = match window_attributes.inner_size {
|
2019-10-19 02:31:26 +11:00
|
|
|
Some(dim) => {
|
|
|
|
let dpi_factor = msg_send![screen, scale];
|
2020-01-04 17:33:07 +11:00
|
|
|
let size = dim.to_logical::<f64>(dpi_factor);
|
2019-10-19 02:31:26 +11:00
|
|
|
CGRect {
|
|
|
|
origin: screen_bounds.origin,
|
|
|
|
size: CGSize {
|
|
|
|
width: size.width as _,
|
|
|
|
height: size.height as _,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
None => screen_bounds,
|
|
|
|
};
|
|
|
|
|
|
|
|
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
|
2019-09-05 07:23:11 +10:00
|
|
|
|
|
|
|
let gl_or_metal_backed = {
|
|
|
|
let view_class: id = msg_send![view, class];
|
|
|
|
let layer_class: id = msg_send![view_class, layerClass];
|
|
|
|
let is_metal: BOOL =
|
|
|
|
msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
|
|
|
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
|
|
|
is_metal == YES || is_gl == YES
|
|
|
|
};
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
let view_controller =
|
|
|
|
view::create_view_controller(&window_attributes, &platform_attributes, view);
|
|
|
|
let window = view::create_window(
|
|
|
|
&window_attributes,
|
|
|
|
&platform_attributes,
|
|
|
|
frame,
|
|
|
|
view_controller,
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
|
|
|
|
let result = Window {
|
|
|
|
inner: Inner {
|
|
|
|
window,
|
|
|
|
view_controller,
|
|
|
|
view,
|
2019-09-05 07:23:11 +10:00
|
|
|
gl_or_metal_backed,
|
2019-05-26 11:10:41 +10:00
|
|
|
},
|
|
|
|
};
|
2019-09-05 07:23:11 +10:00
|
|
|
app_state::set_key_window(window);
|
2019-09-17 04:27:46 +10:00
|
|
|
|
2020-01-06 08:57:32 +11:00
|
|
|
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
2019-09-17 04:27:46 +10:00
|
|
|
// event on window creation if the DPI factor != 1.0
|
2019-10-19 02:31:26 +11:00
|
|
|
let dpi_factor: CGFloat = msg_send![view, contentScaleFactor];
|
2020-01-04 06:52:27 +11:00
|
|
|
let scale_factor: f64 = dpi_factor.into();
|
|
|
|
if scale_factor != 1.0 {
|
2019-09-17 04:27:46 +10:00
|
|
|
let bounds: CGRect = msg_send![view, bounds];
|
|
|
|
let screen: id = msg_send![window, screen];
|
|
|
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
|
|
|
let screen_frame: CGRect =
|
|
|
|
msg_send![view, convertRect:bounds toCoordinateSpace:screen_space];
|
|
|
|
let size = crate::dpi::LogicalSize {
|
|
|
|
width: screen_frame.size.width as _,
|
|
|
|
height: screen_frame.size.height as _,
|
|
|
|
};
|
|
|
|
app_state::handle_nonuser_events(
|
2020-01-04 06:52:27 +11:00
|
|
|
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
|
|
|
window_id: window,
|
|
|
|
scale_factor,
|
|
|
|
suggested_size: size,
|
|
|
|
}))
|
2019-10-19 02:31:26 +11:00
|
|
|
.chain(std::iter::once(EventWrapper::StaticEvent(
|
|
|
|
Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(window.into()),
|
2020-01-04 06:52:27 +11:00
|
|
|
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
2019-10-19 02:31:26 +11:00
|
|
|
},
|
|
|
|
))),
|
2019-09-17 04:27:46 +10:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WindowExtIOS
|
|
|
|
impl Inner {
|
2019-06-22 01:33:15 +10:00
|
|
|
pub fn ui_window(&self) -> id {
|
|
|
|
self.window
|
|
|
|
}
|
|
|
|
pub fn ui_view_controller(&self) -> id {
|
|
|
|
self.view_controller
|
|
|
|
}
|
|
|
|
pub fn ui_view(&self) -> id {
|
|
|
|
self.view
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
|
2020-01-04 06:52:27 +11:00
|
|
|
pub fn set_scale_factor(&self, scale_factor: f64) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-06-22 01:33:15 +10:00
|
|
|
assert!(
|
2020-01-04 06:52:27 +11:00
|
|
|
dpi::validate_scale_factor(scale_factor),
|
|
|
|
"`WindowExtIOS::set_scale_factor` received an invalid hidpi factor"
|
2019-06-22 01:33:15 +10:00
|
|
|
);
|
2020-01-04 06:52:27 +11:00
|
|
|
let scale_factor = scale_factor as CGFloat;
|
|
|
|
let () = msg_send![self.view, setContentScaleFactor: scale_factor];
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
|
|
|
unsafe {
|
|
|
|
let idiom = event_loop::get_idiom();
|
2019-06-22 01:33:15 +10:00
|
|
|
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
|
|
|
|
valid_orientations,
|
|
|
|
idiom,
|
|
|
|
);
|
|
|
|
msg_send![
|
|
|
|
self.view_controller,
|
|
|
|
setSupportedInterfaceOrientations: supported_orientations
|
2019-08-27 08:47:23 +10:00
|
|
|
]
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
2019-07-31 16:57:31 +10:00
|
|
|
|
|
|
|
pub fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
|
|
|
unsafe {
|
2019-08-27 09:56:10 +10:00
|
|
|
let prefers_home_indicator_hidden = if hidden { YES } else { NO };
|
2019-07-31 16:57:31 +10:00
|
|
|
let () = msg_send![
|
|
|
|
self.view_controller,
|
|
|
|
setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
|
|
|
let edges: UIRectEdge = edges.into();
|
|
|
|
unsafe {
|
|
|
|
let () = msg_send![
|
|
|
|
self.view_controller,
|
|
|
|
setPreferredScreenEdgesDeferringSystemGestures: edges
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
2019-08-09 09:10:54 +10:00
|
|
|
|
|
|
|
pub fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
|
|
|
unsafe {
|
|
|
|
let status_bar_hidden = if hidden { YES } else { NO };
|
|
|
|
let () = msg_send![
|
|
|
|
self.view_controller,
|
|
|
|
setPrefersStatusBarHidden: status_bar_hidden
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Inner {
|
|
|
|
// requires main thread
|
|
|
|
unsafe fn screen_frame(&self) -> CGRect {
|
|
|
|
self.to_screen_space(msg_send![self.window, bounds])
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
unsafe fn to_screen_space(&self, rect: CGRect) -> CGRect {
|
|
|
|
let screen: id = msg_send![self.window, screen];
|
|
|
|
if !screen.is_null() {
|
|
|
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
|
|
|
msg_send![self.window, convertRect:rect toCoordinateSpace:screen_space]
|
|
|
|
} else {
|
|
|
|
rect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
unsafe fn from_screen_space(&self, rect: CGRect) -> CGRect {
|
|
|
|
let screen: id = msg_send![self.window, screen];
|
|
|
|
if !screen.is_null() {
|
|
|
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
|
|
|
msg_send![self.window, convertRect:rect fromCoordinateSpace:screen_space]
|
|
|
|
} else {
|
|
|
|
rect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
unsafe fn safe_area_screen_space(&self) -> CGRect {
|
|
|
|
let bounds: CGRect = msg_send![self.window, bounds];
|
2019-08-27 08:47:23 +10:00
|
|
|
if app_state::os_capabilities().safe_area {
|
2019-05-26 11:10:41 +10:00
|
|
|
let safe_area: UIEdgeInsets = msg_send![self.window, safeAreaInsets];
|
|
|
|
let safe_bounds = CGRect {
|
|
|
|
origin: CGPoint {
|
|
|
|
x: bounds.origin.x + safe_area.left,
|
|
|
|
y: bounds.origin.y + safe_area.top,
|
|
|
|
},
|
|
|
|
size: CGSize {
|
|
|
|
width: bounds.size.width - safe_area.left - safe_area.right,
|
|
|
|
height: bounds.size.height - safe_area.top - safe_area.bottom,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
self.to_screen_space(safe_bounds)
|
|
|
|
} else {
|
|
|
|
let screen_frame = self.to_screen_space(bounds);
|
|
|
|
let status_bar_frame: CGRect = {
|
|
|
|
let app: id = msg_send![class!(UIApplication), sharedApplication];
|
2019-06-22 01:33:15 +10:00
|
|
|
assert!(
|
|
|
|
!app.is_null(),
|
|
|
|
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS"
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
msg_send![app, statusBarFrame]
|
|
|
|
};
|
|
|
|
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
|
|
|
|
(screen_frame.origin.y, screen_frame.size.height)
|
|
|
|
} else {
|
|
|
|
let y = status_bar_frame.size.height;
|
2019-06-22 01:33:15 +10:00
|
|
|
let height = screen_frame.size.height
|
|
|
|
- (status_bar_frame.size.height - screen_frame.origin.y);
|
2019-05-26 11:10:41 +10:00
|
|
|
(y, height)
|
|
|
|
};
|
|
|
|
CGRect {
|
|
|
|
origin: CGPoint {
|
|
|
|
x: screen_frame.origin.x,
|
|
|
|
y,
|
|
|
|
},
|
|
|
|
size: CGSize {
|
|
|
|
width: screen_frame.size.width,
|
|
|
|
height,
|
2019-06-22 01:33:15 +10:00
|
|
|
},
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub struct WindowId {
|
|
|
|
window: id,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WindowId {
|
|
|
|
pub unsafe fn dummy() -> Self {
|
|
|
|
WindowId {
|
|
|
|
window: std::ptr::null_mut(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for WindowId {}
|
|
|
|
unsafe impl Sync for WindowId {}
|
|
|
|
|
|
|
|
impl From<&Object> for WindowId {
|
|
|
|
fn from(window: &Object) -> WindowId {
|
2019-06-22 01:33:15 +10:00
|
|
|
WindowId {
|
|
|
|
window: window as *const _ as _,
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&mut Object> for WindowId {
|
|
|
|
fn from(window: &mut Object) -> WindowId {
|
2019-06-22 01:33:15 +10:00
|
|
|
WindowId {
|
|
|
|
window: window as _,
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<id> for WindowId {
|
|
|
|
fn from(window: id) -> WindowId {
|
|
|
|
WindowId { window }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
|
|
|
pub root_view_class: &'static Class,
|
2020-01-04 06:52:27 +11:00
|
|
|
pub scale_factor: Option<f64>,
|
2019-05-26 11:10:41 +10:00
|
|
|
pub valid_orientations: ValidOrientations,
|
2019-07-31 16:57:31 +10:00
|
|
|
pub prefers_home_indicator_hidden: bool,
|
2019-08-09 09:10:54 +10:00
|
|
|
pub prefers_status_bar_hidden: bool,
|
2019-07-31 16:57:31 +10:00
|
|
|
pub preferred_screen_edges_deferring_system_gestures: ScreenEdge,
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PlatformSpecificWindowBuilderAttributes {
|
|
|
|
fn default() -> PlatformSpecificWindowBuilderAttributes {
|
|
|
|
PlatformSpecificWindowBuilderAttributes {
|
|
|
|
root_view_class: class!(UIView),
|
2020-01-04 06:52:27 +11:00
|
|
|
scale_factor: None,
|
2019-05-26 11:10:41 +10:00
|
|
|
valid_orientations: Default::default(),
|
2019-07-31 16:57:31 +10:00
|
|
|
prefers_home_indicator_hidden: false,
|
2019-08-09 09:10:54 +10:00
|
|
|
prefers_status_bar_hidden: false,
|
2019-07-31 16:57:31 +10:00
|
|
|
preferred_screen_edges_deferring_system_gestures: Default::default(),
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|