Skip to content

Commit

Permalink
Add safe abstraction over UIViewController
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Dec 23, 2022
1 parent 6bb1ac9 commit 567271a
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 158 deletions.
54 changes: 1 addition & 53 deletions src/platform_impl/ios/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

use std::convert::TryInto;
use std::ffi::CString;
use std::ops::BitOr;
use std::os::raw::{c_char, c_int};

use objc2::encode::{Encode, Encoding};
use objc2::foundation::{NSInteger, NSUInteger};
use objc2::runtime::Object;
use objc2::{class, msg_send};

use crate::platform::ios::{Idiom, ScreenEdge, ValidOrientations};
use crate::platform::ios::{Idiom, ScreenEdge};

pub type id = *mut Object;
pub const nil: id = 0 as id;
Expand Down Expand Up @@ -115,57 +114,6 @@ impl From<UIUserInterfaceIdiom> for Idiom {
}
}

#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct UIInterfaceOrientationMask(NSUInteger);

unsafe impl Encode for UIInterfaceOrientationMask {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

impl UIInterfaceOrientationMask {
pub const Portrait: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 1);
pub const PortraitUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 2);
pub const LandscapeLeft: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 4);
pub const LandscapeRight: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 3);
pub const Landscape: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0);
pub const AllButUpsideDown: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::Landscape.0 | Self::Portrait.0);
pub const All: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::AllButUpsideDown.0 | Self::PortraitUpsideDown.0);
}

impl BitOr for UIInterfaceOrientationMask {
type Output = Self;

fn bitor(self, rhs: Self) -> Self {
UIInterfaceOrientationMask(self.0 | rhs.0)
}
}

impl UIInterfaceOrientationMask {
pub fn from_valid_orientations_idiom(
valid_orientations: ValidOrientations,
idiom: UIUserInterfaceIdiom,
) -> UIInterfaceOrientationMask {
match (valid_orientations, idiom) {
(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
}
}
}
}

#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIRectEdge(NSUInteger);
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/ios/uikit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
pub(crate) use self::screen_mode::UIScreenMode;
#[allow(unused_imports)]
pub(crate) use self::view::{UIEdgeInsets, UIView};
pub(crate) use self::view_controller::UIViewController;
pub(crate) use self::view_controller::{UIInterfaceOrientationMask, UIViewController};
pub(crate) use self::window::UIWindow;
42 changes: 39 additions & 3 deletions src/platform_impl/ios/uikit/view_controller.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{NSObject, NSUInteger};
use objc2::{extern_class, extern_methods, ClassType};

use super::UIResponder;
use super::{UIView, UIResponder};

extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
Expand All @@ -12,3 +13,38 @@ extern_class!(
type Super = UIResponder;
}
);

extern_methods!(
unsafe impl UIViewController {
#[sel(attemptRotationToDeviceOrientation)]
pub fn attemptRotationToDeviceOrientation();

#[sel(setNeedsStatusBarAppearanceUpdate)]
pub fn setNeedsStatusBarAppearanceUpdate(&self);

#[sel(setNeedsUpdateOfHomeIndicatorAutoHidden)]
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);

#[sel(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);

#[sel(setView:)]
pub fn setView(&self, view: Option<&UIView>);
}
);

bitflags! {
pub struct UIInterfaceOrientationMask: NSUInteger {
const Portrait = 1 << 1;
const PortraitUpsideDown = 1 << 2;
const LandscapeRight = 1 << 3;
const LandscapeLeft = 1 << 4;
const Landscape = Self::LandscapeLeft.bits() | Self::LandscapeRight.bits();
const AllButUpsideDown = Self::Landscape.bits() | Self::Portrait.bits();
const All = Self::AllButUpsideDown.bits() | Self::PortraitUpsideDown.bits();
}
}

unsafe impl Encode for UIInterfaceOrientationMask {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
141 changes: 74 additions & 67 deletions src/platform_impl/ios/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

use objc2::foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject};
use objc2::rc::{Id, Shared};
use objc2::{declare_class, msg_send, msg_send_id, ClassType};
use objc2::{declare_class, msg_send, msg_send_id, extern_methods, ClassType};

use super::uikit::{UIApplication, UIDevice, UIResponder, UIView, UIViewController, UIWindow};
use super::uikit::{UIInterfaceOrientationMask, UIApplication, UIDevice, UIResponder, UIView, UIViewController, UIWindow};
use super::window::WindowId;
use crate::{
dpi::PhysicalPosition,
Expand All @@ -13,12 +13,13 @@ use crate::{
app_state,
event_loop::{EventProxy, EventWrapper},
ffi::{
id, nil, UIForceTouchCapability, UIInterfaceOrientationMask, UIRectEdge, UITouchPhase,
UITouchType,
id, nil, UIForceTouchCapability, UIRectEdge, UITouchPhase,
UITouchType, UIUserInterfaceIdiom
},
window::PlatformSpecificWindowBuilderAttributes,
DeviceId, Fullscreen,
},
platform::ios::ValidOrientations,
window::{WindowAttributes, WindowId as RootWindowId},
};

Expand Down Expand Up @@ -234,7 +235,7 @@ impl WinitView {
}

declare_class!(
struct WinitViewController {
pub(crate) struct WinitViewController {
_prefers_status_bar_hidden: bool,
_prefers_home_indicator_auto_hidden: bool,
_supported_orientations: UIInterfaceOrientationMask,
Expand Down Expand Up @@ -263,9 +264,7 @@ declare_class!(
#[sel(setPrefersStatusBarHidden:)]
fn set_prefers_status_bar_hidden(&mut self, val: bool) {
*self._prefers_status_bar_hidden = val;
unsafe {
let _: () = msg_send![self, setNeedsStatusBarAppearanceUpdate];
}
self.setNeedsStatusBarAppearanceUpdate();
}

#[sel(prefersHomeIndicatorAutoHidden)]
Expand All @@ -278,9 +277,7 @@ declare_class!(
*self._prefers_home_indicator_auto_hidden = val;
let os_capabilities = app_state::os_capabilities();
if os_capabilities.home_indicator_hidden {
unsafe {
let _: () = msg_send![self, setNeedsUpdateOfHomeIndicatorAutoHidden];
}
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
} else {
os_capabilities.home_indicator_hidden_err_msg("ignoring")
}
Expand All @@ -294,12 +291,7 @@ declare_class!(
#[sel(setSupportedInterfaceOrientations:)]
fn set_supported_orientations(&mut self, val: UIInterfaceOrientationMask) {
*self._supported_orientations = val;
unsafe {
let _: () = msg_send![
UIViewController::class(),
attemptRotationToDeviceOrientation
];
}
UIViewController::attemptRotationToDeviceOrientation();
}

#[sel(preferredScreenEdgesDeferringSystemGestures)]
Expand All @@ -312,16 +304,78 @@ declare_class!(
*self._preferred_screen_edges_deferring_system_gestures = val;
let os_capabilities = app_state::os_capabilities();
if os_capabilities.defer_system_gestures {
unsafe {
let _: () = msg_send![self, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures();
} else {
os_capabilities.defer_system_gestures_err_msg("ignoring")
}
}
}
);

extern_methods!(
#[allow(non_snake_case)]
unsafe impl WinitViewController {
#[sel(setPrefersStatusBarHidden:)]
pub(crate) fn setPrefersStatusBarHidden(&self, flag: bool);

#[sel(setSupportedInterfaceOrientations:)]
pub(crate) fn setSupportedInterfaceOrientations(&self, val: UIInterfaceOrientationMask);

#[sel(setPrefersHomeIndicatorAutoHidden:)]
pub(crate) fn setPrefersHomeIndicatorAutoHidden(&self, val: bool);

#[sel(setPreferredScreenEdgesDeferringSystemGestures:)]
pub(crate) fn setPreferredScreenEdgesDeferringSystemGestures(&self, val: UIRectEdge);
}
);

impl WinitViewController {
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.setSupportedInterfaceOrientations(mask);
}

pub(crate) fn new(
mtm: MainThreadMarker,
_window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: &UIView,
) -> Id<Self, Shared> {
let this: Id<Self, Shared> = unsafe {
msg_send_id![msg_send_id![Self::class(), alloc], init]
};

this.setPrefersStatusBarHidden(platform_attributes.prefers_status_bar_hidden);

this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations);

this.setPrefersHomeIndicatorAutoHidden(platform_attributes.prefers_home_indicator_hidden);

this.setPreferredScreenEdgesDeferringSystemGestures(
platform_attributes
.preferred_screen_edges_deferring_system_gestures
.into()
);

this.setView(Some(view));

this
}
}

declare_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct WinitUIWindow {}
Expand Down Expand Up @@ -370,53 +424,6 @@ pub(crate) unsafe fn create_view(
view
}

// requires main thread
pub(crate) unsafe fn create_view_controller(
_window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: id,
) -> id {
let class = WinitViewController::class();

let view_controller: id = msg_send![class, alloc];
assert!(
!view_controller.is_null(),
"Failed to create `UIViewController` instance"
);
let view_controller: id = msg_send![view_controller, init];
assert!(
!view_controller.is_null(),
"Failed to initialize `UIViewController` instance"
);
let status_bar_hidden = platform_attributes.prefers_status_bar_hidden;
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
platform_attributes.valid_orientations,
UIDevice::current(MainThreadMarker::new().unwrap()).userInterfaceIdiom(),
);
let prefers_home_indicator_hidden = platform_attributes.prefers_home_indicator_hidden;
let edges: UIRectEdge = platform_attributes
.preferred_screen_edges_deferring_system_gestures
.into();
let _: () = msg_send![
view_controller,
setPrefersStatusBarHidden: status_bar_hidden
];
let _: () = msg_send![
view_controller,
setSupportedInterfaceOrientations: supported_orientations
];
let _: () = msg_send![
view_controller,
setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden
];
let _: () = msg_send![
view_controller,
setPreferredScreenEdgesDeferringSystemGestures: edges
];
let _: () = msg_send![view_controller, setView: view];
view_controller
}

impl WinitUIWindow {
pub(crate) fn new(
_mtm: MainThreadMarker,
Expand Down
Loading

0 comments on commit 567271a

Please sign in to comment.