mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 05:21:31 +11:00
Clean up UIView override declaration (#2578)
This commit is contained in:
parent
2e4d79f57a
commit
4ed4e918f3
|
@ -6,7 +6,6 @@ mod view_controller;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
pub(crate) use self::responder::UIResponder;
|
pub(crate) use self::responder::UIResponder;
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) use self::view::UIView;
|
pub(crate) use self::view::UIView;
|
||||||
pub(crate) use self::view_controller::UIViewController;
|
pub(crate) use self::view_controller::UIViewController;
|
||||||
pub(crate) use self::window::UIWindow;
|
pub(crate) use self::window::UIWindow;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use objc2::declare::ClassBuilder;
|
|
||||||
use objc2::foundation::NSObject;
|
use objc2::foundation::NSObject;
|
||||||
use objc2::runtime::{Bool, Class, Object, Sel};
|
use objc2::{class, declare_class, msg_send, ClassType};
|
||||||
use objc2::{class, declare_class, msg_send, sel, ClassType};
|
|
||||||
|
|
||||||
use super::uikit::{UIResponder, UIViewController, UIWindow};
|
use super::uikit::{UIResponder, UIView, UIViewController, UIWindow};
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::PhysicalPosition,
|
dpi::PhysicalPosition,
|
||||||
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
||||||
|
@ -20,13 +18,20 @@ use crate::{
|
||||||
window::{WindowAttributes, WindowId as RootWindowId},
|
window::{WindowAttributes, WindowId as RootWindowId},
|
||||||
};
|
};
|
||||||
|
|
||||||
// requires main thread
|
declare_class!(
|
||||||
unsafe fn get_view_class() -> &'static Class {
|
struct WinitView {}
|
||||||
static mut CLASS: Option<&'static Class> = None;
|
|
||||||
if CLASS.is_none() {
|
unsafe impl ClassType for WinitView {
|
||||||
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
|
#[inherits(UIResponder, NSObject)]
|
||||||
|
type Super = UIView;
|
||||||
|
const NAME: &'static str = "WinitUIView";
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl WinitView {
|
||||||
|
#[sel(drawRect:)]
|
||||||
|
fn draw_rect(&self, rect: CGRect) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let window: id = msg_send![object, window];
|
let window: id = msg_send![self, window];
|
||||||
assert!(!window.is_null());
|
assert!(!window.is_null());
|
||||||
app_state::handle_nonuser_events(
|
app_state::handle_nonuser_events(
|
||||||
std::iter::once(EventWrapper::StaticEvent(Event::RedrawRequested(
|
std::iter::once(EventWrapper::StaticEvent(Event::RedrawRequested(
|
||||||
|
@ -36,23 +41,22 @@ unsafe fn get_view_class() -> &'static Class {
|
||||||
Event::RedrawEventsCleared,
|
Event::RedrawEventsCleared,
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
let superclass: &'static Class = msg_send![object, superclass];
|
let _: () = msg_send![super(self), drawRect: rect];
|
||||||
let _: () = msg_send![super(object, superclass), drawRect: rect];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn layout_subviews(object: &Object, _: Sel) {
|
#[sel(layoutSubviews)]
|
||||||
|
fn layout_subviews(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let superclass: &'static Class = msg_send![object, superclass];
|
let _: () = msg_send![super(self), layoutSubviews];
|
||||||
let _: () = msg_send![super(object, superclass), layoutSubviews];
|
|
||||||
|
|
||||||
let window: id = msg_send![object, window];
|
let window: id = msg_send![self, window];
|
||||||
assert!(!window.is_null());
|
assert!(!window.is_null());
|
||||||
let window_bounds: CGRect = msg_send![window, bounds];
|
let window_bounds: CGRect = msg_send![window, bounds];
|
||||||
let screen: id = msg_send![window, screen];
|
let screen: id = msg_send![window, screen];
|
||||||
let screen_space: id = msg_send![screen, coordinateSpace];
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
||||||
let screen_frame: CGRect = msg_send![
|
let screen_frame: CGRect = msg_send![
|
||||||
object,
|
self,
|
||||||
convertRect: window_bounds,
|
convertRect: window_bounds,
|
||||||
toCoordinateSpace: screen_space,
|
toCoordinateSpace: screen_space,
|
||||||
];
|
];
|
||||||
|
@ -66,9 +70,9 @@ unsafe fn get_view_class() -> &'static Class {
|
||||||
// If the app is started in landscape, the view frame and window bounds can be mismatched.
|
// If the app is started in landscape, the view frame and window bounds can be mismatched.
|
||||||
// The view frame will be in portrait and the window bounds in landscape. So apply the
|
// The view frame will be in portrait and the window bounds in landscape. So apply the
|
||||||
// window bounds to the view frame to make it consistent.
|
// window bounds to the view frame to make it consistent.
|
||||||
let view_frame: CGRect = msg_send![object, frame];
|
let view_frame: CGRect = msg_send![self, frame];
|
||||||
if view_frame != window_bounds {
|
if view_frame != window_bounds {
|
||||||
let _: () = msg_send![object, setFrame: window_bounds];
|
let _: () = msg_send![self, setFrame: window_bounds];
|
||||||
}
|
}
|
||||||
|
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
|
@ -78,20 +82,12 @@ unsafe fn get_view_class() -> &'static Class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn set_content_scale_factor(
|
#[sel(setContentScaleFactor:)]
|
||||||
object: &mut Object,
|
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
||||||
_: Sel,
|
|
||||||
untrusted_scale_factor: CGFloat,
|
|
||||||
) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let superclass: &'static Class = msg_send![&*object, superclass];
|
let _: () = msg_send![super(self), setContentScaleFactor: untrusted_scale_factor];
|
||||||
let _: () = msg_send![
|
|
||||||
super(&mut *object, superclass),
|
|
||||||
setContentScaleFactor: untrusted_scale_factor
|
|
||||||
];
|
|
||||||
let object = &*object; // Immutable for rest of method
|
|
||||||
|
|
||||||
let window: id = msg_send![object, window];
|
let window: id = msg_send![self, window];
|
||||||
// `window` is null when `setContentScaleFactor` is invoked prior to `[UIWindow
|
// `window` is null when `setContentScaleFactor` is invoked prior to `[UIWindow
|
||||||
// makeKeyAndVisible]` at window creation time (either manually or internally by
|
// makeKeyAndVisible]` at window creation time (either manually or internally by
|
||||||
// UIKit when the `UIView` is first created), in which case we send no events here
|
// UIKit when the `UIView` is first created), in which case we send no events here
|
||||||
|
@ -101,7 +97,7 @@ unsafe fn get_view_class() -> &'static Class {
|
||||||
// `setContentScaleFactor` may be called with a value of 0, which means "reset the
|
// `setContentScaleFactor` may be called with a value of 0, which means "reset the
|
||||||
// content scale factor to a device-specific default value", so we can't use the
|
// content scale factor to a device-specific default value", so we can't use the
|
||||||
// parameter here. We can query the actual factor using the getter
|
// parameter here. We can query the actual factor using the getter
|
||||||
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
|
let scale_factor: CGFloat = msg_send![self, contentScaleFactor];
|
||||||
assert!(
|
assert!(
|
||||||
!scale_factor.is_nan()
|
!scale_factor.is_nan()
|
||||||
&& scale_factor.is_finite()
|
&& scale_factor.is_finite()
|
||||||
|
@ -110,11 +106,11 @@ unsafe fn get_view_class() -> &'static Class {
|
||||||
"invalid scale_factor set on UIView",
|
"invalid scale_factor set on UIView",
|
||||||
);
|
);
|
||||||
let scale_factor = scale_factor as f64;
|
let scale_factor = scale_factor as f64;
|
||||||
let bounds: CGRect = msg_send![object, bounds];
|
let bounds: CGRect = msg_send![self, bounds];
|
||||||
let screen: id = msg_send![window, screen];
|
let screen: id = msg_send![window, screen];
|
||||||
let screen_space: id = msg_send![screen, coordinateSpace];
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
||||||
let screen_frame: CGRect =
|
let screen_frame: CGRect =
|
||||||
msg_send![object, convertRect: bounds, toCoordinateSpace: screen_space];
|
msg_send![self, convertRect: bounds, toCoordinateSpace: screen_space];
|
||||||
let size = crate::dpi::LogicalSize {
|
let size = crate::dpi::LogicalSize {
|
||||||
width: screen_frame.size.width as _,
|
width: screen_frame.size.width as _,
|
||||||
height: screen_frame.size.height as _,
|
height: screen_frame.size.height as _,
|
||||||
|
@ -135,109 +131,101 @@ unsafe fn get_view_class() -> &'static Class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
|
#[sel(touchesBegan:withEvent:)]
|
||||||
unsafe {
|
fn touches_began(&self, touches: id, _: id) {
|
||||||
let window: id = msg_send![object, window];
|
self.handle_touches(touches)
|
||||||
assert!(!window.is_null());
|
|
||||||
let uiscreen: id = msg_send![window, screen];
|
|
||||||
let touches_enum: id = msg_send![touches, objectEnumerator];
|
|
||||||
let mut touch_events = Vec::new();
|
|
||||||
let os_supports_force = app_state::os_capabilities().force_touch;
|
|
||||||
loop {
|
|
||||||
let touch: id = msg_send![touches_enum, nextObject];
|
|
||||||
if touch == nil {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let logical_location: CGPoint = msg_send![touch, locationInView: nil];
|
|
||||||
let touch_type: UITouchType = msg_send![touch, type];
|
|
||||||
let force = if os_supports_force {
|
|
||||||
let trait_collection: id = msg_send![object, traitCollection];
|
|
||||||
let touch_capability: UIForceTouchCapability =
|
|
||||||
msg_send![trait_collection, forceTouchCapability];
|
|
||||||
// Both the OS _and_ the device need to be checked for force touch support.
|
|
||||||
if touch_capability == UIForceTouchCapability::Available {
|
|
||||||
let force: CGFloat = msg_send![touch, force];
|
|
||||||
let max_possible_force: CGFloat =
|
|
||||||
msg_send![touch, maximumPossibleForce];
|
|
||||||
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
|
||||||
let angle: CGFloat = msg_send![touch, altitudeAngle];
|
|
||||||
Some(angle as _)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
Some(Force::Calibrated {
|
|
||||||
force: force as _,
|
|
||||||
max_possible_force: max_possible_force as _,
|
|
||||||
altitude_angle,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let touch_id = touch as u64;
|
|
||||||
let phase: UITouchPhase = msg_send![touch, phase];
|
|
||||||
let phase = match phase {
|
|
||||||
UITouchPhase::Began => TouchPhase::Started,
|
|
||||||
UITouchPhase::Moved => TouchPhase::Moved,
|
|
||||||
// 2 is UITouchPhase::Stationary and is not expected here
|
|
||||||
UITouchPhase::Ended => TouchPhase::Ended,
|
|
||||||
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
|
||||||
_ => panic!("unexpected touch phase: {:?}", phase as i32),
|
|
||||||
};
|
|
||||||
|
|
||||||
let physical_location = {
|
|
||||||
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
|
|
||||||
PhysicalPosition::from_logical::<(f64, f64), f64>(
|
|
||||||
(logical_location.x as _, logical_location.y as _),
|
|
||||||
scale_factor as f64,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(window.into()),
|
|
||||||
event: WindowEvent::Touch(Touch {
|
|
||||||
device_id: RootDeviceId(DeviceId { uiscreen }),
|
|
||||||
id: touch_id,
|
|
||||||
location: physical_location,
|
|
||||||
force,
|
|
||||||
phase,
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
app_state::handle_nonuser_events(touch_events);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decl = ClassBuilder::new("WinitUIView", class!(UIView))
|
#[sel(touchesMoved:withEvent:)]
|
||||||
.expect("Failed to declare class `WinitUIView`");
|
fn touches_moved(&self, touches: id, _: id) {
|
||||||
decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _));
|
self.handle_touches(touches)
|
||||||
decl.add_method(sel!(layoutSubviews), layout_subviews as extern "C" fn(_, _));
|
}
|
||||||
decl.add_method(
|
|
||||||
sel!(setContentScaleFactor:),
|
|
||||||
set_content_scale_factor as extern "C" fn(_, _, _),
|
|
||||||
);
|
|
||||||
|
|
||||||
decl.add_method(
|
#[sel(touchesEnded:withEvent:)]
|
||||||
sel!(touchesBegan:withEvent:),
|
fn touches_ended(&self, touches: id, _: id) {
|
||||||
handle_touches as extern "C" fn(_, _, _, _),
|
self.handle_touches(touches)
|
||||||
);
|
}
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesMoved:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(_, _, _, _),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesEnded:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(_, _, _, _),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesCancelled:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(_, _, _, _),
|
|
||||||
);
|
|
||||||
|
|
||||||
CLASS = Some(decl.register());
|
#[sel(touchesCancelled:withEvent:)]
|
||||||
|
fn touches_cancelled(&self, touches: id, _: id) {
|
||||||
|
self.handle_touches(touches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl WinitView {
|
||||||
|
fn handle_touches(&self, touches: id) {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![self, window];
|
||||||
|
assert!(!window.is_null());
|
||||||
|
let uiscreen: id = msg_send![window, screen];
|
||||||
|
let touches_enum: id = msg_send![touches, objectEnumerator];
|
||||||
|
let mut touch_events = Vec::new();
|
||||||
|
let os_supports_force = app_state::os_capabilities().force_touch;
|
||||||
|
loop {
|
||||||
|
let touch: id = msg_send![touches_enum, nextObject];
|
||||||
|
if touch == nil {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let logical_location: CGPoint = msg_send![touch, locationInView: nil];
|
||||||
|
let touch_type: UITouchType = msg_send![touch, type];
|
||||||
|
let force = if os_supports_force {
|
||||||
|
let trait_collection: id = msg_send![self, traitCollection];
|
||||||
|
let touch_capability: UIForceTouchCapability =
|
||||||
|
msg_send![trait_collection, forceTouchCapability];
|
||||||
|
// Both the OS _and_ the device need to be checked for force touch support.
|
||||||
|
if touch_capability == UIForceTouchCapability::Available {
|
||||||
|
let force: CGFloat = msg_send![touch, force];
|
||||||
|
let max_possible_force: CGFloat = msg_send![touch, maximumPossibleForce];
|
||||||
|
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
||||||
|
let angle: CGFloat = msg_send![touch, altitudeAngle];
|
||||||
|
Some(angle as _)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Some(Force::Calibrated {
|
||||||
|
force: force as _,
|
||||||
|
max_possible_force: max_possible_force as _,
|
||||||
|
altitude_angle,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let touch_id = touch as u64;
|
||||||
|
let phase: UITouchPhase = msg_send![touch, phase];
|
||||||
|
let phase = match phase {
|
||||||
|
UITouchPhase::Began => TouchPhase::Started,
|
||||||
|
UITouchPhase::Moved => TouchPhase::Moved,
|
||||||
|
// 2 is UITouchPhase::Stationary and is not expected here
|
||||||
|
UITouchPhase::Ended => TouchPhase::Ended,
|
||||||
|
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
||||||
|
_ => panic!("unexpected touch phase: {:?}", phase as i32),
|
||||||
|
};
|
||||||
|
|
||||||
|
let physical_location = {
|
||||||
|
let scale_factor: CGFloat = msg_send![self, contentScaleFactor];
|
||||||
|
PhysicalPosition::from_logical::<(f64, f64), f64>(
|
||||||
|
(logical_location.x as _, logical_location.y as _),
|
||||||
|
scale_factor as f64,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.into()),
|
||||||
|
event: WindowEvent::Touch(Touch {
|
||||||
|
device_id: RootDeviceId(DeviceId { uiscreen }),
|
||||||
|
id: touch_id,
|
||||||
|
location: physical_location,
|
||||||
|
force,
|
||||||
|
phase,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
app_state::handle_nonuser_events(touch_events);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CLASS.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
|
@ -368,13 +356,11 @@ pub(crate) unsafe fn create_view(
|
||||||
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
) -> id {
|
) -> id {
|
||||||
let class = get_view_class();
|
let view: id = msg_send![WinitView::class(), alloc];
|
||||||
|
|
||||||
let view: id = msg_send![class, alloc];
|
|
||||||
assert!(!view.is_null(), "Failed to create `UIView` instance");
|
assert!(!view.is_null(), "Failed to create `UIView` instance");
|
||||||
let view: id = msg_send![view, initWithFrame: frame];
|
let view: id = msg_send![view, initWithFrame: frame];
|
||||||
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
|
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
|
||||||
let _: () = msg_send![view, setMultipleTouchEnabled: Bool::YES];
|
let _: () = msg_send![view, setMultipleTouchEnabled: true];
|
||||||
if let Some(scale_factor) = platform_attributes.scale_factor {
|
if let Some(scale_factor) = platform_attributes.scale_factor {
|
||||||
let _: () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat];
|
let _: () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat];
|
||||||
}
|
}
|
||||||
|
@ -400,14 +386,13 @@ pub(crate) unsafe fn create_view_controller(
|
||||||
!view_controller.is_null(),
|
!view_controller.is_null(),
|
||||||
"Failed to initialize `UIViewController` instance"
|
"Failed to initialize `UIViewController` instance"
|
||||||
);
|
);
|
||||||
let status_bar_hidden = Bool::new(platform_attributes.prefers_status_bar_hidden);
|
let status_bar_hidden = platform_attributes.prefers_status_bar_hidden;
|
||||||
let idiom = event_loop::get_idiom();
|
let idiom = event_loop::get_idiom();
|
||||||
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
|
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
|
||||||
platform_attributes.valid_orientations,
|
platform_attributes.valid_orientations,
|
||||||
idiom,
|
idiom,
|
||||||
);
|
);
|
||||||
let prefers_home_indicator_hidden =
|
let prefers_home_indicator_hidden = platform_attributes.prefers_home_indicator_hidden;
|
||||||
Bool::new(platform_attributes.prefers_home_indicator_hidden);
|
|
||||||
let edges: UIRectEdge = platform_attributes
|
let edges: UIRectEdge = platform_attributes
|
||||||
.preferred_screen_edges_deferring_system_gestures
|
.preferred_screen_edges_deferring_system_gestures
|
||||||
.into();
|
.into();
|
||||||
|
|
Loading…
Reference in a new issue