Update objc2 and icrate versions (#3256)

This commit is contained in:
Mads Marquart 2023-12-23 18:04:24 +01:00 committed by GitHub
parent 745cfaab2c
commit 7d5bee767c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 286 additions and 374 deletions

View File

@ -86,13 +86,13 @@ ndk-sys = "0.5.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3"
objc2 = "0.4.1"
objc2 = "0.5.0"
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1"
[target.'cfg(target_os = "macos")'.dependencies.icrate]
version = "0.0.4"
version = "0.1.0"
features = [
"dispatch",
"Foundation",
@ -108,7 +108,7 @@ features = [
]
[target.'cfg(target_os = "ios")'.dependencies.icrate]
version = "0.0.4"
version = "0.1.0"
features = [
"dispatch",
"Foundation",

View File

@ -8,6 +8,7 @@ use std::{
use icrate::Foundation::{MainThreadBound, MainThreadMarker, NSInteger};
use objc2::mutability::IsRetainable;
use objc2::rc::Id;
use objc2::Message;
use super::uikit::{UIScreen, UIScreenMode};
use crate::{
@ -20,16 +21,15 @@ use crate::{
#[derive(Debug)]
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
impl<T: IsRetainable> Clone for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
fn clone(&self) -> Self {
Self(
self.0
.get_on_main(|inner, mtm| MainThreadBound::new(Id::clone(inner), mtm)),
)
Self(MainThreadMarker::run_on_main(|mtm| {
MainThreadBound::new(Id::clone(self.0.get(mtm)), mtm)
}))
}
}
impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
// SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() };
@ -37,7 +37,7 @@ impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
}
}
impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
fn eq(&self, other: &Self) -> bool {
// SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() };
@ -45,7 +45,7 @@ impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
}
}
impl<T: IsRetainable> Eq for MainThreadBoundDelegateImpls<T> {}
impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct VideoMode {
@ -100,11 +100,9 @@ pub struct MonitorHandle {
impl Clone for MonitorHandle {
fn clone(&self) -> Self {
Self {
ui_screen: self
.ui_screen
.get_on_main(|inner, mtm| MainThreadBound::new(inner.clone(), mtm)),
}
MainThreadMarker::run_on_main(|mtm| Self {
ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm),
})
}
}
@ -168,16 +166,16 @@ impl MonitorHandle {
}
pub fn name(&self) -> Option<String> {
self.ui_screen.get_on_main(|ui_screen, mtm| {
MainThreadMarker::run_on_main(|mtm| {
let main = UIScreen::main(mtm);
if *ui_screen == main {
if *self.ui_screen(mtm) == main {
Some("Primary".to_string())
} else if *ui_screen == main.mirroredScreen() {
} else if *self.ui_screen(mtm) == main.mirroredScreen() {
Some("Mirrored".to_string())
} else {
UIScreen::screens(mtm)
.iter()
.position(|rhs| rhs == &**ui_screen)
.position(|rhs| rhs == &**self.ui_screen(mtm))
.map(|idx| idx.to_string())
}
})
@ -186,31 +184,32 @@ impl MonitorHandle {
pub fn size(&self) -> PhysicalSize<u32> {
let bounds = self
.ui_screen
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
.get_on_main(|ui_screen| ui_screen.nativeBounds());
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
}
pub fn position(&self) -> PhysicalPosition<i32> {
let bounds = self
.ui_screen
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
.get_on_main(|ui_screen| ui_screen.nativeBounds());
(bounds.origin.x as f64, bounds.origin.y as f64).into()
}
pub fn scale_factor(&self) -> f64 {
self.ui_screen
.get_on_main(|ui_screen, _| ui_screen.nativeScale()) as f64
.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64
}
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
Some(
self.ui_screen
.get_on_main(|ui_screen, _| refresh_rate_millihertz(ui_screen)),
.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)),
)
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.ui_screen.get_on_main(|ui_screen, mtm| {
MainThreadMarker::run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm);
// Use Ord impl of RootVideoMode
let modes: BTreeSet<_> = ui_screen
@ -230,8 +229,12 @@ impl MonitorHandle {
}
pub fn preferred_video_mode(&self) -> VideoMode {
self.ui_screen.get_on_main(|ui_screen, mtm| {
VideoMode::new(ui_screen.clone(), ui_screen.preferredMode().unwrap(), mtm)
MainThreadMarker::run_on_main(|mtm| {
VideoMode::new(
self.ui_screen(mtm).clone(),
self.ui_screen(mtm).preferredMode().unwrap(),
mtm,
)
})
}
}

View File

@ -1,12 +1,12 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::Cell;
use std::ptr::NonNull;
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::Id;
use objc2::runtime::AnyClass;
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
use objc2::{
declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType, DeclaredClass,
};
use super::app_state::{self, EventWrapper};
use super::uikit::{
@ -37,6 +37,8 @@ declare_class!(
const NAME: &'static str = "WinitUIView";
}
impl DeclaredClass for WinitView {}
unsafe impl WinitView {
#[method(drawRect:)]
fn draw_rect(&self, rect: CGRect) {
@ -274,11 +276,7 @@ pub struct ViewControllerState {
}
declare_class!(
pub(crate) struct WinitViewController {
state: IvarDrop<Box<ViewControllerState>, "_state">,
}
mod view_controller_ivars;
pub(crate) struct WinitViewController;
unsafe impl ClassType for WinitViewController {
#[inherits(UIResponder, NSObject)]
@ -287,28 +285,8 @@ declare_class!(
const NAME: &'static str = "WinitUIViewController";
}
unsafe impl WinitViewController {
#[method(init)]
unsafe fn init(this: *mut Self) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = msg_send![super(this), init];
this.map(|this| {
// These are set in WinitViewController::new, it's just to set them
// to _something_.
Ivar::write(
&mut this.state,
Box::new(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,
),
}),
);
NonNull::from(this)
})
}
impl DeclaredClass for WinitViewController {
type Ivars = ViewControllerState;
}
unsafe impl WinitViewController {
@ -319,27 +297,27 @@ declare_class!(
#[method(prefersStatusBarHidden)]
fn prefers_status_bar_hidden(&self) -> bool {
self.state.prefers_status_bar_hidden.get()
self.ivars().prefers_status_bar_hidden.get()
}
#[method(preferredStatusBarStyle)]
fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
self.state.preferred_status_bar_style.get()
self.ivars().preferred_status_bar_style.get()
}
#[method(prefersHomeIndicatorAutoHidden)]
fn prefers_home_indicator_auto_hidden(&self) -> bool {
self.state.prefers_home_indicator_auto_hidden.get()
self.ivars().prefers_home_indicator_auto_hidden.get()
}
#[method(supportedInterfaceOrientations)]
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
self.state.supported_orientations.get()
self.ivars().supported_orientations.get()
}
#[method(preferredScreenEdgesDeferringSystemGestures)]
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
self.state
self.ivars()
.preferred_screen_edges_deferring_system_gestures
.get()
}
@ -348,17 +326,17 @@ declare_class!(
impl WinitViewController {
pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) {
self.state.prefers_status_bar_hidden.set(val);
self.ivars().prefers_status_bar_hidden.set(val);
self.setNeedsStatusBarAppearanceUpdate();
}
pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) {
self.state.preferred_status_bar_style.set(val);
self.ivars().preferred_status_bar_style.set(val);
self.setNeedsStatusBarAppearanceUpdate();
}
pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) {
self.state.prefers_home_indicator_auto_hidden.set(val);
self.ivars().prefers_home_indicator_auto_hidden.set(val);
let os_capabilities = app_state::os_capabilities();
if os_capabilities.home_indicator_hidden {
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
@ -368,7 +346,7 @@ impl WinitViewController {
}
pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) {
self.state
self.ivars()
.preferred_screen_edges_deferring_system_gestures
.set(val);
let os_capabilities = app_state::os_capabilities();
@ -401,7 +379,7 @@ impl WinitViewController {
| UIInterfaceOrientationMask::PortraitUpsideDown
}
};
self.state.supported_orientations.set(mask);
self.ivars().supported_orientations.set(mask);
UIViewController::attemptRotationToDeviceOrientation();
}
@ -411,7 +389,15 @@ impl WinitViewController {
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: &UIView,
) -> Id<Self> {
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), init] };
// 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(platform_attributes.prefers_status_bar_hidden);
@ -446,6 +432,8 @@ declare_class!(
const NAME: &'static str = "WinitUIWindow";
}
impl DeclaredClass for WinitUIWindow {}
unsafe impl WinitUIWindow {
#[method(becomeKeyWindow)]
fn become_key_window(&self) {
@ -518,6 +506,8 @@ declare_class!(
const NAME: &'static str = "WinitApplicationDelegate";
}
impl DeclaredClass for WinitApplicationDelegate {}
// UIApplicationDelegate protocol
unsafe impl WinitApplicationDelegate {
#[method(application:didFinishLaunchingWithOptions:)]

View File

@ -520,7 +520,7 @@ impl Window {
}
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
self.inner.get_on_main(|inner, _mtm| f(inner))
self.inner.get_on_main(|inner| f(inner))
}
#[cfg(feature = "rwh_06")]

View File

@ -1,14 +1,13 @@
#![allow(clippy::unnecessary_cast)]
use icrate::Foundation::NSObject;
use objc2::{declare_class, msg_send, mutability, ClassType};
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
use super::{app_state::AppState, DEVICE_ID};
use crate::event::{DeviceEvent, ElementState, Event};
declare_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(super) struct WinitApplication;
unsafe impl ClassType for WinitApplication {
@ -18,6 +17,8 @@ declare_class!(
const NAME: &'static str = "WinitApplication";
}
impl DeclaredClass for WinitApplication {}
unsafe impl WinitApplication {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)

View File

@ -1,23 +1,20 @@
use std::ptr::NonNull;
use icrate::Foundation::NSObject;
use objc2::declare::{IvarBool, IvarEncode};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType};
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use super::app_state::AppState;
use super::appkit::NSApplicationActivationPolicy;
declare_class!(
#[derive(Debug)]
pub(super) struct ApplicationDelegate {
activation_policy: IvarEncode<NSApplicationActivationPolicy, "_activation_policy">,
default_menu: IvarBool<"_default_menu">,
activate_ignoring_other_apps: IvarBool<"_activate_ignoring_other_apps">,
}
#[derive(Debug)]
pub(super) struct State {
activation_policy: NSApplicationActivationPolicy,
default_menu: bool,
activate_ignoring_other_apps: bool,
}
mod ivars;
declare_class!(
pub(super) struct ApplicationDelegate;
unsafe impl ClassType for ApplicationDelegate {
type Super = NSObject;
@ -25,30 +22,18 @@ declare_class!(
const NAME: &'static str = "WinitApplicationDelegate";
}
unsafe impl ApplicationDelegate {
#[method(initWithActivationPolicy:defaultMenu:activateIgnoringOtherApps:)]
unsafe fn init(
this: *mut Self,
activation_policy: NSApplicationActivationPolicy,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
this.map(|this| {
*this.activation_policy = activation_policy;
*this.default_menu = default_menu;
*this.activate_ignoring_other_apps = activate_ignoring_other_apps;
NonNull::from(this)
})
}
impl DeclaredClass for ApplicationDelegate {
type Ivars = State;
}
unsafe impl ApplicationDelegate {
#[method(applicationDidFinishLaunching:)]
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationDidFinishLaunching:");
AppState::launched(
*self.activation_policy,
*self.default_menu,
*self.activate_ignoring_other_apps,
self.ivars().activation_policy,
self.ivars().default_menu,
self.ivars().activate_ignoring_other_apps,
);
}
@ -67,13 +52,11 @@ impl ApplicationDelegate {
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Id<Self> {
unsafe {
msg_send_id![
Self::alloc(),
initWithActivationPolicy: activation_policy,
defaultMenu: default_menu,
activateIgnoringOtherApps: activate_ignoring_other_apps,
]
}
let this = Self::alloc().set_ivars(State {
activation_policy,
default_menu,
activate_ignoring_other_apps,
});
unsafe { msg_send_id![super(this), init] }
}
}

View File

@ -1,8 +1,8 @@
use once_cell::sync::Lazy;
use icrate::ns_string;
use icrate::Foundation::{
NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize, NSString,
ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize,
NSString,
};
use objc2::rc::{DefaultId, Id};
use objc2::runtime::Sel;

View File

@ -1,5 +1,6 @@
use icrate::ns_string;
use icrate::Foundation::{CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString};
use icrate::Foundation::{
ns_string, CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString,
};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};

View File

@ -1,5 +1,4 @@
use icrate::ns_string;
use icrate::Foundation::{NSProcessInfo, NSString};
use icrate::Foundation::{ns_string, NSProcessInfo, NSString};
use objc2::rc::Id;
use objc2::runtime::Sel;
use objc2::sel;

View File

@ -1,17 +1,16 @@
#![allow(clippy::unnecessary_cast)]
use std::boxed::Box;
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::ptr::NonNull;
use icrate::Foundation::{
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::{Id, WeakId};
use objc2::runtime::{AnyObject, Sel};
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType};
use objc2::{
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use super::{
appkit::{
@ -140,18 +139,13 @@ pub struct ViewState {
marked_text: RefCell<Id<NSMutableAttributedString>>,
accepts_first_mouse: bool,
// Weak reference because the window keeps a strong reference to the view
_ns_window: WeakId<WinitWindow>,
}
declare_class!(
#[derive(Debug)]
#[allow(non_snake_case)]
pub(super) struct WinitView {
// Weak reference because the window keeps a strong reference to the view
_ns_window: IvarDrop<Box<WeakId<WinitWindow>>, "__ns_window">,
state: IvarDrop<Box<ViewState>, "_state">,
}
mod ivars;
pub(super) struct WinitView;
unsafe impl ClassType for WinitView {
#[inherits(NSResponder, NSObject)]
@ -160,73 +154,33 @@ declare_class!(
const NAME: &'static str = "WinitView";
}
unsafe impl WinitView {
#[method(initWithId:acceptsFirstMouse:)]
unsafe fn init_with_id(
this: *mut Self,
window: &WinitWindow,
accepts_first_mouse: bool,
) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
this.map(|this| {
let state = ViewState {
accepts_first_mouse,
..Default::default()
};
Ivar::write(
&mut this._ns_window,
Box::new(WeakId::new(&window.retain())),
);
Ivar::write(&mut this.state, Box::new(state));
this.setPostsFrameChangedNotifications(true);
let notification_center: &AnyObject =
unsafe { msg_send![class!(NSNotificationCenter), defaultCenter] };
// About frame change
let frame_did_change_notification_name =
NSString::from_str("NSViewFrameDidChangeNotification");
#[allow(clippy::let_unit_value)]
unsafe {
let _: () = msg_send![
notification_center,
addObserver: &*this,
selector: sel!(frameDidChange:),
name: &*frame_did_change_notification_name,
object: &*this,
];
}
*this.state.input_source.borrow_mut() = this.current_input_source();
NonNull::from(this)
})
}
impl DeclaredClass for WinitView {
type Ivars = ViewState;
}
unsafe impl WinitView {
#[method(viewDidMoveToWindow)]
fn view_did_move_to_window(&self) {
trace_scope!("viewDidMoveToWindow");
if let Some(tracking_rect) = self.state.tracking_rect.take() {
if let Some(tracking_rect) = self.ivars().tracking_rect.take() {
self.removeTrackingRect(tracking_rect);
}
let rect = self.frame();
let tracking_rect = self.add_tracking_rect(rect, false);
self.state.tracking_rect.set(Some(tracking_rect));
self.ivars().tracking_rect.set(Some(tracking_rect));
}
#[method(frameDidChange:)]
fn frame_did_change(&self, _event: &NSEvent) {
trace_scope!("frameDidChange:");
if let Some(tracking_rect) = self.state.tracking_rect.take() {
if let Some(tracking_rect) = self.ivars().tracking_rect.take() {
self.removeTrackingRect(tracking_rect);
}
let rect = self.frame();
let tracking_rect = self.add_tracking_rect(rect, false);
self.state.tracking_rect.set(Some(tracking_rect));
self.ivars().tracking_rect.set(Some(tracking_rect));
// Emit resize event here rather than from windowDidResize because:
// 1. When a new window is created as a tab, the frame size may change without a window resize occurring.
@ -241,7 +195,7 @@ declare_class!(
trace_scope!("drawRect:");
// It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
if let Some(window) = self._ns_window.load() {
if let Some(window) = self.ivars()._ns_window.load() {
AppState::handle_redraw(WindowId(window.id()));
}
@ -270,7 +224,7 @@ declare_class!(
fn reset_cursor_rects(&self) {
trace_scope!("resetCursorRects");
let bounds = self.bounds();
let cursor_state = self.state.cursor_state.borrow();
let cursor_state = self.ivars().cursor_state.borrow();
// We correctly invoke `addCursorRect` only from inside `resetCursorRects`
if cursor_state.visible {
self.addCursorRect(bounds, &cursor_state.cursor);
@ -284,13 +238,13 @@ declare_class!(
#[method(hasMarkedText)]
fn has_marked_text(&self) -> bool {
trace_scope!("hasMarkedText");
self.state.marked_text.borrow().length() > 0
self.ivars().marked_text.borrow().length() > 0
}
#[method(markedRange)]
fn marked_range(&self) -> NSRange {
trace_scope!("markedRange");
let length = self.state.marked_text.borrow().length();
let length = self.ivars().marked_text.borrow().length();
if length > 0 {
NSRange::new(0, length)
} else {
@ -333,19 +287,19 @@ declare_class!(
};
// Update marked text.
*self.state.marked_text.borrow_mut() = marked_text;
*self.ivars().marked_text.borrow_mut() = marked_text;
// Notify IME is active if application still doesn't know it.
if self.state.ime_state.get() == ImeState::Disabled {
*self.state.input_source.borrow_mut() = self.current_input_source();
if self.ivars().ime_state.get() == ImeState::Disabled {
*self.ivars().input_source.borrow_mut() = self.current_input_source();
self.queue_event(WindowEvent::Ime(Ime::Enabled));
}
if self.hasMarkedText() {
self.state.ime_state.set(ImeState::Preedit);
self.ivars().ime_state.set(ImeState::Preedit);
} else {
// In case the preedit was cleared, set IME into the Ground state.
self.state.ime_state.set(ImeState::Ground);
self.ivars().ime_state.set(ImeState::Ground);
}
// Empty string basically means that there's no preedit, so indicate that by sending
@ -363,15 +317,15 @@ declare_class!(
#[method(unmarkText)]
fn unmark_text(&self) {
trace_scope!("unmarkText");
*self.state.marked_text.borrow_mut() = NSMutableAttributedString::new();
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
let input_context = self.inputContext().expect("input context");
input_context.discardMarkedText();
self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None)));
if self.is_ime_enabled() {
// Leave the Preedit self.state
self.state.ime_state.set(ImeState::Ground);
// Leave the Preedit self.ivars()
self.ivars().ime_state.set(ImeState::Ground);
} else {
warn!("Expected to have IME enabled when receiving unmarkText");
}
@ -410,9 +364,9 @@ declare_class!(
let content_rect = window.contentRectForFrameRect(window.frame());
let base_x = content_rect.origin.x as f64;
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
let x = base_x + self.state.ime_position.get().x;
let y = base_y - self.state.ime_position.get().y;
let LogicalSize { width, height } = self.state.ime_size.get();
let x = base_x + self.ivars().ime_position.get().x;
let y = base_y - self.ivars().ime_position.get().y;
let LogicalSize { width, height } = self.ivars().ime_size.get();
NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(width, height))
}
@ -437,7 +391,7 @@ declare_class!(
if self.hasMarkedText() && self.is_ime_enabled() && !is_control {
self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None)));
self.queue_event(WindowEvent::Ime(Ime::Commit(string)));
self.state.ime_state.set(ImeState::Commited);
self.ivars().ime_state.set(ImeState::Commited);
}
}
@ -449,15 +403,15 @@ declare_class!(
// We shouldn't forward any character from just commited text, since we'll end up sending
// it twice with some IMEs like Korean one. We'll also always send `Enter` in that case,
// which is not desired given it was used to confirm IME input.
if self.state.ime_state.get() == ImeState::Commited {
if self.ivars().ime_state.get() == ImeState::Commited {
return;
}
self.state.forward_key_to_app.set(true);
self.ivars().forward_key_to_app.set(true);
if self.hasMarkedText() && self.state.ime_state.get() == ImeState::Preedit {
if self.hasMarkedText() && self.ivars().ime_state.get() == ImeState::Preedit {
// Leave preedit so that we also report the key-up for this key.
self.state.ime_state.set(ImeState::Ground);
self.ivars().ime_state.set(ImeState::Ground);
}
}
}
@ -467,19 +421,19 @@ declare_class!(
fn key_down(&self, event: &NSEvent) {
trace_scope!("keyDown:");
{
let mut prev_input_source = self.state.input_source.borrow_mut();
let mut prev_input_source = self.ivars().input_source.borrow_mut();
let current_input_source = self.current_input_source();
if *prev_input_source != current_input_source && self.is_ime_enabled() {
*prev_input_source = current_input_source;
drop(prev_input_source);
self.state.ime_state.set(ImeState::Disabled);
self.ivars().ime_state.set(ImeState::Disabled);
self.queue_event(WindowEvent::Ime(Ime::Disabled));
}
}
// Get the characters from the event.
let old_ime_state = self.state.ime_state.get();
self.state.forward_key_to_app.set(false);
let old_ime_state = self.ivars().ime_state.get();
self.ivars().forward_key_to_app.set(false);
let event = replace_event(event, self.window().option_as_alt());
// The `interpretKeyEvents` function might call
@ -488,31 +442,31 @@ declare_class!(
// we must send the `KeyboardInput` event during IME if it triggered
// `doCommandBySelector`. (doCommandBySelector means that the keyboard input
// is not handled by IME and should be handled by the application)
if self.state.ime_allowed.get() {
if self.ivars().ime_allowed.get() {
let events_for_nsview = NSArray::from_slice(&[&*event]);
unsafe { self.interpretKeyEvents(&events_for_nsview) };
// If the text was commited we must treat the next keyboard event as IME related.
if self.state.ime_state.get() == ImeState::Commited {
if self.ivars().ime_state.get() == ImeState::Commited {
// Remove any marked text, so normal input can continue.
*self.state.marked_text.borrow_mut() = NSMutableAttributedString::new();
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
}
}
self.update_modifiers(&event, false);
let had_ime_input = match self.state.ime_state.get() {
let had_ime_input = match self.ivars().ime_state.get() {
ImeState::Commited => {
// Allow normal input after the commit.
self.state.ime_state.set(ImeState::Ground);
self.ivars().ime_state.set(ImeState::Ground);
true
}
ImeState::Preedit => true,
// `key_down` could result in preedit clear, so compare old and current state.
_ => old_ime_state != self.state.ime_state.get(),
_ => old_ime_state != self.ivars().ime_state.get(),
};
if !had_ime_input || self.state.forward_key_to_app.get() {
if !had_ime_input || self.ivars().forward_key_to_app.get() {
let key_event = create_key_event(&event, true, event.is_a_repeat(), None);
self.queue_event(WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
@ -531,7 +485,7 @@ declare_class!(
// We want to send keyboard input when we are currently in the ground state.
if matches!(
self.state.ime_state.get(),
self.ivars().ime_state.get(),
ImeState::Ground | ImeState::Disabled
) {
self.queue_event(WindowEvent::KeyboardInput {
@ -792,20 +746,40 @@ declare_class!(
#[method(acceptsFirstMouse:)]
fn accepts_first_mouse(&self, _event: &NSEvent) -> bool {
trace_scope!("acceptsFirstMouse:");
self.state.accepts_first_mouse
self.ivars().accepts_first_mouse
}
}
);
impl WinitView {
pub(super) fn new(window: &WinitWindow, accepts_first_mouse: bool) -> Id<Self> {
let this = Self::alloc().set_ivars(ViewState {
accepts_first_mouse,
_ns_window: WeakId::new(&window.retain()),
..Default::default()
});
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
this.setPostsFrameChangedNotifications(true);
let notification_center: &AnyObject =
unsafe { msg_send![class!(NSNotificationCenter), defaultCenter] };
// About frame change
let frame_did_change_notification_name =
NSString::from_str("NSViewFrameDidChangeNotification");
#[allow(clippy::let_unit_value)]
unsafe {
msg_send_id![
Self::alloc(),
initWithId: window,
acceptsFirstMouse: accepts_first_mouse,
]
let _: () = msg_send![
notification_center,
addObserver: &*this,
selector: sel!(frameDidChange:),
name: &*frame_did_change_notification_name,
object: &*this,
];
}
*this.ivars().input_source.borrow_mut() = this.current_input_source();
this
}
fn window(&self) -> Id<WinitWindow> {
@ -814,7 +788,10 @@ impl WinitView {
// (which is incompatible with `frameDidChange:`)
//
// unsafe { msg_send_id![self, window] }
self._ns_window.load().expect("view to have a window")
self.ivars()
._ns_window
.load()
.expect("view to have a window")
}
fn window_id(&self) -> WindowId {
@ -842,7 +819,7 @@ impl WinitView {
}
fn is_ime_enabled(&self) -> bool {
!matches!(self.state.ime_state.get(), ImeState::Disabled)
!matches!(self.ivars().ime_state.get(), ImeState::Disabled)
}
fn current_input_source(&self) -> String {
@ -854,7 +831,7 @@ impl WinitView {
}
pub(super) fn set_cursor_icon(&self, icon: Id<NSCursor>) {
let mut cursor_state = self.state.cursor_state.borrow_mut();
let mut cursor_state = self.ivars().cursor_state.borrow_mut();
cursor_state.cursor = icon;
}
@ -862,7 +839,7 @@ impl WinitView {
///
/// Returns whether the state changed.
pub(super) fn set_cursor_visible(&self, visible: bool) -> bool {
let mut cursor_state = self.state.cursor_state.borrow_mut();
let mut cursor_state = self.ivars().cursor_state.borrow_mut();
if visible != cursor_state.visible {
cursor_state.visible = visible;
true
@ -872,19 +849,19 @@ impl WinitView {
}
pub(super) fn set_ime_allowed(&self, ime_allowed: bool) {
if self.state.ime_allowed.get() == ime_allowed {
if self.ivars().ime_allowed.get() == ime_allowed {
return;
}
self.state.ime_allowed.set(ime_allowed);
if self.state.ime_allowed.get() {
self.ivars().ime_allowed.set(ime_allowed);
if self.ivars().ime_allowed.get() {
return;
}
// Clear markedText
*self.state.marked_text.borrow_mut() = NSMutableAttributedString::new();
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
if self.state.ime_state.get() != ImeState::Disabled {
self.state.ime_state.set(ImeState::Disabled);
if self.ivars().ime_state.get() != ImeState::Disabled {
self.ivars().ime_state.set(ImeState::Disabled);
self.queue_event(WindowEvent::Ime(Ime::Disabled));
}
}
@ -894,17 +871,17 @@ impl WinitView {
position: LogicalPosition<f64>,
size: LogicalSize<f64>,
) {
self.state.ime_position.set(position);
self.state.ime_size.set(size);
self.ivars().ime_position.set(position);
self.ivars().ime_size.set(size);
let input_context = self.inputContext().expect("input context");
input_context.invalidateCharacterCoordinates();
}
/// Reset modifiers and emit a synthetic ModifiersChanged event if deemed necessary.
pub(super) fn reset_modifiers(&self) {
if !self.state.modifiers.get().state().is_empty() {
self.state.modifiers.set(Modifiers::default());
self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers.get()));
if !self.ivars().modifiers.get().state().is_empty() {
self.ivars().modifiers.set(Modifiers::default());
self.queue_event(WindowEvent::ModifiersChanged(self.ivars().modifiers.get()));
}
}
@ -913,8 +890,8 @@ impl WinitView {
use ElementState::{Pressed, Released};
let current_modifiers = event_mods(ns_event);
let prev_modifiers = self.state.modifiers.get();
self.state.modifiers.set(current_modifiers);
let prev_modifiers = self.ivars().modifiers.get();
self.ivars().modifiers.set(current_modifiers);
// This function was called form the flagsChanged event, which is triggered
// when the user presses/releases a modifier even if the same kind of modifier
@ -943,7 +920,7 @@ impl WinitView {
event.location = code_to_location(physical_key);
let location_mask = ModLocationMask::from_location(event.location);
let mut phys_mod_state = self.state.phys_modifiers.borrow_mut();
let mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut();
let phys_mod = phys_mod_state
.entry(key)
.or_insert(ModLocationMask::empty());
@ -1021,7 +998,7 @@ impl WinitView {
return;
}
self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers.get()));
self.queue_event(WindowEvent::ModifiersChanged(self.ivars().modifiers.get()));
}
fn mouse_click(&self, event: &NSEvent, button_state: ElementState) {

View File

@ -3,8 +3,6 @@
use std::collections::VecDeque;
use std::f64;
use std::ops;
use std::os::raw::c_void;
use std::ptr::NonNull;
use std::sync::{Mutex, MutexGuard};
use crate::{
@ -36,9 +34,8 @@ use icrate::Foundation::{
CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSInteger, NSObject, NSPoint,
NSRect, NSSize, NSString,
};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::{autoreleasepool, Id};
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType};
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use super::appkit::{
NSApp, NSAppKitVersion, NSAppearance, NSApplicationPresentationOptions, NSBackingStoreType,
@ -58,7 +55,7 @@ pub(crate) struct Window {
impl Drop for Window {
fn drop(&mut self) {
self.window
.get_on_main(|window, _| autoreleasepool(|_| window.close()))
.get_on_main(|window| autoreleasepool(|_| window.close()))
}
}
@ -86,7 +83,7 @@ impl Window {
&self,
f: impl FnOnce(&WinitWindow) -> R + Send,
) -> R {
self.window.get_on_main(|window, _mtm| f(window))
self.window.get_on_main(|window| f(window))
}
#[cfg(feature = "rwh_06")]
@ -159,12 +156,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
declare_class!(
#[derive(Debug)]
pub struct WinitWindow {
// TODO: Fix unnecessary boxing here
shared_state: IvarDrop<Box<Mutex<SharedState>>, "_shared_state">,
}
mod ivars;
pub struct WinitWindow;
unsafe impl ClassType for WinitWindow {
#[inherits(NSResponder, NSObject)]
@ -173,38 +165,8 @@ declare_class!(
const NAME: &'static str = "WinitWindow";
}
unsafe impl WinitWindow {
#[method(initWithContentRect:styleMask:state:)]
unsafe fn init(
this: *mut Self,
frame: NSRect,
mask: NSWindowStyleMask,
state: *mut c_void,
) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = unsafe {
msg_send![
super(this),
initWithContentRect: frame,
styleMask: mask,
backing: NSBackingStoreType::NSBackingStoreBuffered,
defer: false,
]
};
this.map(|this| {
// SAFETY: The pointer originally came from `Box::into_raw`.
Ivar::write(&mut this.shared_state, unsafe {
Box::from_raw(state as *mut Mutex<SharedState>)
});
// It is imperative to correct memory management that we
// disable the extra release that would otherwise happen when
// calling `clone` on the window.
this.setReleasedWhenClosed(false);
NonNull::from(this)
})
}
impl DeclaredClass for WinitWindow {
type Ivars = Mutex<SharedState>;
}
unsafe impl WinitWindow {
@ -384,18 +346,23 @@ impl WinitWindow {
..Default::default()
};
// Pass the state through FFI to the method declared on the class
let state_ptr: *mut c_void = Box::into_raw(Box::new(Mutex::new(state))).cast();
let this = WinitWindow::alloc().set_ivars(Mutex::new(state));
let this: Option<Id<Self>> = unsafe {
msg_send_id![
WinitWindow::alloc(),
super(this),
initWithContentRect: frame,
styleMask: masks,
state: state_ptr,
backing: NSBackingStoreType::NSBackingStoreBuffered,
defer: false,
]
};
let this = this?;
// It is very important for correct memory management that we
// disable the extra release that would otherwise happen when
// calling `close` on the window.
this.setReleasedWhenClosed(false);
let resize_increments = match attrs
.resize_increments
.map(|i| i.to_logical::<f64>(this.scale_factor()))
@ -576,7 +543,7 @@ impl WinitWindow {
&self,
called_from_fn: &'static str,
) -> SharedStateMutexGuard<'_> {
SharedStateMutexGuard::new(self.shared_state.lock().unwrap(), called_from_fn)
SharedStateMutexGuard::new(self.ivars().lock().unwrap(), called_from_fn)
}
fn set_style_mask(&self, mask: NSWindowStyleMask) {

View File

@ -1,12 +1,13 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::Cell;
use std::ptr::{self, NonNull};
use std::ptr;
use icrate::Foundation::{NSArray, NSObject, NSSize, NSString};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::{autoreleasepool, Id};
use objc2::runtime::AnyObject;
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType};
use objc2::{
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use super::appkit::{
NSApplicationPresentationOptions, NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionState,
@ -24,7 +25,9 @@ use crate::{
};
#[derive(Debug)]
pub struct State {
pub(crate) struct State {
window: Id<WinitWindow>,
// This is set when WindowBuilder::with_fullscreen was set,
// see comments of `window_did_fail_to_enter_fullscreen`
initial_fullscreen: Cell<bool>,
@ -37,17 +40,7 @@ pub struct State {
}
declare_class!(
#[derive(Debug)]
pub(crate) struct WinitWindowDelegate {
window: IvarDrop<Id<WinitWindow>, "_window">,
// TODO: It may be possible for delegate methods to be called
// asynchronously, causing data races panics?
// TODO: Remove unnecessary boxing here
state: IvarDrop<Box<State>, "_state">,
}
mod ivars;
pub(crate) struct WinitWindowDelegate;
unsafe impl ClassType for WinitWindowDelegate {
type Super = NSObject;
@ -55,50 +48,8 @@ declare_class!(
const NAME: &'static str = "WinitWindowDelegate";
}
unsafe impl WinitWindowDelegate {
#[method(initWithWindow:initialFullscreen:)]
unsafe fn init_with_winit(
this: *mut Self,
window: &WinitWindow,
initial_fullscreen: bool,
) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
this.map(|this| {
let scale_factor = window.scale_factor();
Ivar::write(&mut this.window, window.retain());
Ivar::write(
&mut this.state,
Box::new(State {
initial_fullscreen: Cell::new(initial_fullscreen),
previous_position: Cell::new(None),
previous_scale_factor: Cell::new(scale_factor),
}),
);
if scale_factor != 1.0 {
this.queue_static_scale_factor_changed_event();
}
this.window.setDelegate(Some(this));
// Enable theme change event
let notification_center: Id<AnyObject> =
unsafe { msg_send_id![class!(NSDistributedNotificationCenter), defaultCenter] };
let notification_name =
NSString::from_str("AppleInterfaceThemeChangedNotification");
let _: () = unsafe {
msg_send![
&notification_center,
addObserver: &*this
selector: sel!(effectiveAppearanceDidChange:)
name: &*notification_name
object: ptr::null::<AnyObject>()
]
};
NonNull::from(this)
})
}
impl DeclaredClass for WinitWindowDelegate {
type Ivars = State;
}
// NSWindowDelegate + NSDraggingDestination protocols
@ -117,7 +68,7 @@ declare_class!(
autoreleasepool(|_| {
// Since El Capitan, we need to be careful that delegate methods can't
// be called after the window closes.
self.window.setDelegate(None);
self.ivars().window.setDelegate(None);
});
self.queue_event(WindowEvent::Destroyed);
}
@ -134,16 +85,19 @@ declare_class!(
trace_scope!("windowWillStartLiveResize:");
let increments = self
.ivars()
.window
.lock_shared_state("window_will_enter_fullscreen")
.resize_increments;
self.window.set_resize_increments_inner(increments);
self.ivars().window.set_resize_increments_inner(increments);
}
#[method(windowDidEndLiveResize:)]
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidEndLiveResize:");
self.window.set_resize_increments_inner(NSSize::new(1., 1.));
self.ivars()
.window
.set_resize_increments_inner(NSSize::new(1., 1.));
}
// This won't be triggered if the move was part of a resize.
@ -177,7 +131,7 @@ declare_class!(
// NSWindowDelegate, and as a result a tracked modifiers state can quite
// easily fall out of synchrony with reality. This requires us to emit
// a synthetic ModifiersChanged event when we lose focus.
self.window.view().reset_modifiers();
self.ivars().window.view().reset_modifiers();
self.queue_event(WindowEvent::Focused(false));
}
@ -246,9 +200,10 @@ declare_class!(
trace_scope!("windowWillEnterFullScreen:");
let mut shared_state = self
.ivars()
.window
.lock_shared_state("window_will_enter_fullscreen");
shared_state.maximized = self.window.is_zoomed();
shared_state.maximized = self.ivars().window.is_zoomed();
let fullscreen = shared_state.fullscreen.as_ref();
match fullscreen {
// Exclusive mode sets the state in `set_fullscreen` as the user
@ -262,7 +217,7 @@ declare_class!(
// Otherwise, we must've reached fullscreen by the user clicking
// on the green fullscreen button. Update state!
None => {
let current_monitor = self.window.current_monitor_inner();
let current_monitor = self.ivars().window.current_monitor_inner();
shared_state.fullscreen = Some(Fullscreen::Borderless(current_monitor))
}
}
@ -274,7 +229,10 @@ declare_class!(
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillExitFullScreen:");
let mut shared_state = self.window.lock_shared_state("window_will_exit_fullscreen");
let mut shared_state = self
.ivars()
.window
.lock_shared_state("window_will_exit_fullscreen");
shared_state.in_fullscreen_transition = true;
}
@ -295,6 +253,7 @@ declare_class!(
// user-provided options are ignored in exclusive fullscreen.
let mut options = proposed_options;
let shared_state = self
.ivars()
.window
.lock_shared_state("window_will_use_fullscreen_presentation_options");
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
@ -310,13 +269,16 @@ declare_class!(
#[method(windowDidEnterFullScreen:)]
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidEnterFullScreen:");
self.state.initial_fullscreen.set(false);
let mut shared_state = self.window.lock_shared_state("window_did_enter_fullscreen");
self.ivars().initial_fullscreen.set(false);
let mut shared_state = self
.ivars()
.window
.lock_shared_state("window_did_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
self.window.set_fullscreen(target_fullscreen);
self.ivars().window.set_fullscreen(target_fullscreen);
}
}
@ -325,13 +287,16 @@ declare_class!(
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidExitFullScreen:");
self.window.restore_state_from_fullscreen();
let mut shared_state = self.window.lock_shared_state("window_did_exit_fullscreen");
self.ivars().window.restore_state_from_fullscreen();
let mut shared_state = self
.ivars()
.window
.lock_shared_state("window_did_exit_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
self.window.set_fullscreen(target_fullscreen);
self.ivars().window.set_fullscreen(target_fullscreen);
}
}
@ -355,22 +320,23 @@ declare_class!(
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidFailToEnterFullScreen:");
let mut shared_state = self
.ivars()
.window
.lock_shared_state("window_did_fail_to_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
shared_state.target_fullscreen = None;
if self.state.initial_fullscreen.get() {
if self.ivars().initial_fullscreen.get() {
#[allow(clippy::let_unit_value)]
unsafe {
let _: () = msg_send![
&*self.window,
&*self.ivars().window,
performSelector: sel!(toggleFullScreen:),
withObject: ptr::null::<AnyObject>(),
afterDelay: 0.5,
];
};
} else {
self.window.restore_state_from_fullscreen();
self.ivars().window.restore_state_from_fullscreen();
}
}
@ -380,6 +346,7 @@ declare_class!(
trace_scope!("windowDidChangeOcclusionState:");
self.queue_event(WindowEvent::Occluded(
!self
.ivars()
.window
.occlusionState()
.contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible),
@ -404,6 +371,7 @@ declare_class!(
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
let theme = get_ns_theme();
let mut shared_state = self
.ivars()
.window
.lock_shared_state("effective_appearance_did_change");
let current_theme = shared_state.current_theme;
@ -418,12 +386,13 @@ declare_class!(
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeScreen:");
let is_simple_fullscreen = self
.ivars()
.window
.lock_shared_state("window_did_change_screen")
.is_simple_fullscreen;
if is_simple_fullscreen {
if let Some(screen) = self.window.screen() {
self.window.setFrame_display(screen.frame(), true);
if let Some(screen) = self.ivars().window.screen() {
self.ivars().window.setFrame_display(screen.frame(), true);
}
}
}
@ -432,52 +401,74 @@ declare_class!(
impl WinitWindowDelegate {
pub fn new(window: &WinitWindow, initial_fullscreen: bool) -> Id<Self> {
unsafe {
msg_send_id![
Self::alloc(),
initWithWindow: window,
initialFullscreen: initial_fullscreen,
]
let scale_factor = window.scale_factor();
let this = Self::alloc().set_ivars(State {
window: window.retain(),
initial_fullscreen: Cell::new(initial_fullscreen),
previous_position: Cell::new(None),
previous_scale_factor: Cell::new(scale_factor),
});
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
if scale_factor != 1.0 {
this.queue_static_scale_factor_changed_event();
}
this.ivars().window.setDelegate(Some(&this));
// Enable theme change event
let notification_center: Id<AnyObject> =
unsafe { msg_send_id![class!(NSDistributedNotificationCenter), defaultCenter] };
let notification_name = NSString::from_str("AppleInterfaceThemeChangedNotification");
let _: () = unsafe {
msg_send![
&notification_center,
addObserver: &*this
selector: sel!(effectiveAppearanceDidChange:)
name: &*notification_name
object: ptr::null::<AnyObject>()
]
};
this
}
pub(crate) fn queue_event(&self, event: WindowEvent) {
let event = Event::WindowEvent {
window_id: WindowId(self.window.id()),
window_id: WindowId(self.ivars().window.id()),
event,
};
AppState::queue_event(event);
}
fn queue_static_scale_factor_changed_event(&self) {
let scale_factor = self.window.scale_factor();
if scale_factor == self.state.previous_scale_factor.get() {
let scale_factor = self.ivars().window.scale_factor();
if scale_factor == self.ivars().previous_scale_factor.get() {
return;
};
self.state.previous_scale_factor.set(scale_factor);
self.ivars().previous_scale_factor.set(scale_factor);
let suggested_size = self.view_size();
AppState::queue_static_scale_factor_changed_event(
self.window.clone(),
self.ivars().window.clone(),
suggested_size.to_physical(scale_factor),
scale_factor,
);
}
fn emit_move_event(&self) {
let rect = self.window.frame();
let rect = self.ivars().window.frame();
let x = rect.origin.x as f64;
let y = util::bottom_left_to_top_left(rect);
if self.state.previous_position.get() != Some((x, y)) {
self.state.previous_position.set(Some((x, y)));
let scale_factor = self.window.scale_factor();
if self.ivars().previous_position.get() != Some((x, y)) {
self.ivars().previous_position.set(Some((x, y)));
let scale_factor = self.ivars().window.scale_factor();
let physical_pos = LogicalPosition::<f64>::from((x, y)).to_physical(scale_factor);
self.queue_event(WindowEvent::Moved(physical_pos));
}
}
fn view_size(&self) -> LogicalSize<f64> {
let size = self.window.contentView().frame().size;
let size = self.ivars().window.contentView().frame().size;
LogicalSize::new(size.width as f64, size.height as f64)
}
}