Update objc2 and icrate versions (#3256)
This commit is contained in:
parent
745cfaab2c
commit
7d5bee767c
12 changed files with 286 additions and 374 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue