1use crate::{
2 executor,
3 geometry::{
4 rect::RectF,
5 vector::{vec2f, Vector2F},
6 },
7 keymap_matcher::Keystroke,
8 mac::platform::NSViewLayerContentsRedrawDuringViewResize,
9 platform::{
10 self,
11 mac::{geometry::RectFExt, renderer::Renderer, screen::Screen},
12 Event, WindowBounds,
13 },
14 InputHandler, KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseButtonEvent,
15 MouseMovedEvent, Scene, WindowKind,
16};
17use block::ConcreteBlock;
18use cocoa::{
19 appkit::{
20 CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
21 NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
22 NSWindowStyleMask,
23 },
24 base::{id, nil},
25 foundation::{
26 NSAutoreleasePool, NSInteger, NSNotFound, NSPoint, NSRect, NSSize, NSString, NSUInteger,
27 },
28};
29use core_graphics::display::CGRect;
30use ctor::ctor;
31use foreign_types::ForeignTypeRef;
32use objc::{
33 class,
34 declare::ClassDecl,
35 msg_send,
36 runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
37 sel, sel_impl,
38};
39use postage::oneshot;
40use smol::Timer;
41use std::{
42 any::Any,
43 cell::{Cell, RefCell},
44 convert::TryInto,
45 ffi::{c_void, CStr},
46 mem,
47 ops::Range,
48 os::raw::c_char,
49 ptr,
50 rc::{Rc, Weak},
51 sync::Arc,
52 time::Duration,
53};
54
55const WINDOW_STATE_IVAR: &str = "windowState";
56
57static mut WINDOW_CLASS: *const Class = ptr::null();
58static mut PANEL_CLASS: *const Class = ptr::null();
59static mut VIEW_CLASS: *const Class = ptr::null();
60
61#[allow(non_upper_case_globals)]
62const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask =
63 unsafe { NSWindowStyleMask::from_bits_unchecked(1 << 7) };
64#[allow(non_upper_case_globals)]
65const NSNormalWindowLevel: NSInteger = 0;
66#[allow(non_upper_case_globals)]
67const NSPopUpWindowLevel: NSInteger = 101;
68#[allow(non_upper_case_globals)]
69const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01;
70#[allow(non_upper_case_globals)]
71const NSTrackingMouseMoved: NSUInteger = 0x02;
72#[allow(non_upper_case_globals)]
73const NSTrackingActiveAlways: NSUInteger = 0x80;
74#[allow(non_upper_case_globals)]
75const NSTrackingInVisibleRect: NSUInteger = 0x200;
76#[allow(non_upper_case_globals)]
77const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
78
79#[repr(C)]
80#[derive(Copy, Clone, Debug)]
81struct NSRange {
82 pub location: NSUInteger,
83 pub length: NSUInteger,
84}
85
86impl NSRange {
87 fn invalid() -> Self {
88 Self {
89 location: NSNotFound as NSUInteger,
90 length: 0,
91 }
92 }
93
94 fn is_valid(&self) -> bool {
95 self.location != NSNotFound as NSUInteger
96 }
97
98 fn to_range(self) -> Option<Range<usize>> {
99 if self.is_valid() {
100 let start = self.location as usize;
101 let end = start + self.length as usize;
102 Some(start..end)
103 } else {
104 None
105 }
106 }
107}
108
109impl From<Range<usize>> for NSRange {
110 fn from(range: Range<usize>) -> Self {
111 NSRange {
112 location: range.start as NSUInteger,
113 length: range.len() as NSUInteger,
114 }
115 }
116}
117
118unsafe impl objc::Encode for NSRange {
119 fn encode() -> objc::Encoding {
120 let encoding = format!(
121 "{{NSRange={}{}}}",
122 NSUInteger::encode().as_str(),
123 NSUInteger::encode().as_str()
124 );
125 unsafe { objc::Encoding::from_str(&encoding) }
126 }
127}
128
129#[ctor]
130unsafe fn build_classes() {
131 WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
132 PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel));
133 VIEW_CLASS = {
134 let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
135 decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
136
137 decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
138
139 decl.add_method(
140 sel!(performKeyEquivalent:),
141 handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
142 );
143 decl.add_method(
144 sel!(keyDown:),
145 handle_key_down as extern "C" fn(&Object, Sel, id),
146 );
147 decl.add_method(
148 sel!(mouseDown:),
149 handle_view_event as extern "C" fn(&Object, Sel, id),
150 );
151 decl.add_method(
152 sel!(mouseUp:),
153 handle_view_event as extern "C" fn(&Object, Sel, id),
154 );
155 decl.add_method(
156 sel!(rightMouseDown:),
157 handle_view_event as extern "C" fn(&Object, Sel, id),
158 );
159 decl.add_method(
160 sel!(rightMouseUp:),
161 handle_view_event as extern "C" fn(&Object, Sel, id),
162 );
163 decl.add_method(
164 sel!(otherMouseDown:),
165 handle_view_event as extern "C" fn(&Object, Sel, id),
166 );
167 decl.add_method(
168 sel!(otherMouseUp:),
169 handle_view_event as extern "C" fn(&Object, Sel, id),
170 );
171 decl.add_method(
172 sel!(mouseMoved:),
173 handle_view_event as extern "C" fn(&Object, Sel, id),
174 );
175 decl.add_method(
176 sel!(mouseExited:),
177 handle_view_event as extern "C" fn(&Object, Sel, id),
178 );
179 decl.add_method(
180 sel!(mouseDragged:),
181 handle_view_event as extern "C" fn(&Object, Sel, id),
182 );
183 decl.add_method(
184 sel!(scrollWheel:),
185 handle_view_event as extern "C" fn(&Object, Sel, id),
186 );
187 decl.add_method(
188 sel!(flagsChanged:),
189 handle_view_event as extern "C" fn(&Object, Sel, id),
190 );
191 decl.add_method(
192 sel!(cancelOperation:),
193 cancel_operation as extern "C" fn(&Object, Sel, id),
194 );
195
196 decl.add_method(
197 sel!(makeBackingLayer),
198 make_backing_layer as extern "C" fn(&Object, Sel) -> id,
199 );
200
201 decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
202 decl.add_method(
203 sel!(viewDidChangeBackingProperties),
204 view_did_change_backing_properties as extern "C" fn(&Object, Sel),
205 );
206 decl.add_method(
207 sel!(setFrameSize:),
208 set_frame_size as extern "C" fn(&Object, Sel, NSSize),
209 );
210 decl.add_method(
211 sel!(displayLayer:),
212 display_layer as extern "C" fn(&Object, Sel, id),
213 );
214
215 decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());
216 decl.add_method(
217 sel!(validAttributesForMarkedText),
218 valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
219 );
220 decl.add_method(
221 sel!(hasMarkedText),
222 has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
223 );
224 decl.add_method(
225 sel!(markedRange),
226 marked_range as extern "C" fn(&Object, Sel) -> NSRange,
227 );
228 decl.add_method(
229 sel!(selectedRange),
230 selected_range as extern "C" fn(&Object, Sel) -> NSRange,
231 );
232 decl.add_method(
233 sel!(firstRectForCharacterRange:actualRange:),
234 first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,
235 );
236 decl.add_method(
237 sel!(insertText:replacementRange:),
238 insert_text as extern "C" fn(&Object, Sel, id, NSRange),
239 );
240 decl.add_method(
241 sel!(setMarkedText:selectedRange:replacementRange:),
242 set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),
243 );
244 decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
245 decl.add_method(
246 sel!(attributedSubstringForProposedRange:actualRange:),
247 attributed_substring_for_proposed_range
248 as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
249 );
250 decl.add_method(
251 sel!(viewDidChangeEffectiveAppearance),
252 view_did_change_effective_appearance as extern "C" fn(&Object, Sel),
253 );
254
255 // Suppress beep on keystrokes with modifier keys.
256 decl.add_method(
257 sel!(doCommandBySelector:),
258 do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
259 );
260
261 decl.add_method(
262 sel!(acceptsFirstMouse:),
263 accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
264 );
265
266 decl.register()
267 };
268}
269
270unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
271 let mut decl = ClassDecl::new(name, superclass).unwrap();
272 decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
273 decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
274 decl.add_method(
275 sel!(canBecomeMainWindow),
276 yes as extern "C" fn(&Object, Sel) -> BOOL,
277 );
278 decl.add_method(
279 sel!(canBecomeKeyWindow),
280 yes as extern "C" fn(&Object, Sel) -> BOOL,
281 );
282 decl.add_method(
283 sel!(sendEvent:),
284 send_event as extern "C" fn(&Object, Sel, id),
285 );
286 decl.add_method(
287 sel!(windowDidResize:),
288 window_did_resize as extern "C" fn(&Object, Sel, id),
289 );
290 decl.add_method(
291 sel!(windowWillEnterFullScreen:),
292 window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
293 );
294 decl.add_method(
295 sel!(windowWillExitFullScreen:),
296 window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
297 );
298 decl.add_method(
299 sel!(windowDidBecomeKey:),
300 window_did_change_key_status as extern "C" fn(&Object, Sel, id),
301 );
302 decl.add_method(
303 sel!(windowDidResignKey:),
304 window_did_change_key_status as extern "C" fn(&Object, Sel, id),
305 );
306 decl.add_method(
307 sel!(windowShouldClose:),
308 window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
309 );
310 decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
311 decl.register()
312}
313
314pub struct Window(Rc<RefCell<WindowState>>);
315
316///Used to track what the IME does when we send it a keystroke.
317///This is only used to handle the case where the IME mysteriously
318///swallows certain keys.
319///
320///Basically a direct copy of the approach that WezTerm uses in:
321///github.com/wez/wezterm : d5755f3e : window/src/os/macos/window.rs
322enum ImeState {
323 Continue,
324 Acted,
325 None,
326}
327
328struct WindowState {
329 id: usize,
330 native_window: id,
331 kind: WindowKind,
332 event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
333 activate_callback: Option<Box<dyn FnMut(bool)>>,
334 resize_callback: Option<Box<dyn FnMut()>>,
335 fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
336 should_close_callback: Option<Box<dyn FnMut() -> bool>>,
337 close_callback: Option<Box<dyn FnOnce()>>,
338 appearance_changed_callback: Option<Box<dyn FnMut()>>,
339 input_handler: Option<Box<dyn InputHandler>>,
340 pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
341 performed_key_equivalent: bool,
342 synthetic_drag_counter: usize,
343 executor: Rc<executor::Foreground>,
344 scene_to_render: Option<Scene>,
345 renderer: Renderer,
346 last_fresh_keydown: Option<Keystroke>,
347 traffic_light_position: Option<Vector2F>,
348 previous_modifiers_changed_event: Option<Event>,
349 //State tracking what the IME did after the last request
350 ime_state: ImeState,
351 //Retains the last IME Text
352 ime_text: Option<String>,
353}
354
355struct InsertText {
356 replacement_range: Option<Range<usize>>,
357 text: String,
358}
359
360impl Window {
361 pub fn open(
362 id: usize,
363 options: platform::WindowOptions,
364 executor: Rc<executor::Foreground>,
365 fonts: Arc<dyn platform::FontSystem>,
366 ) -> Self {
367 unsafe {
368 let pool = NSAutoreleasePool::new(nil);
369
370 let mut style_mask;
371 if let Some(titlebar) = options.titlebar.as_ref() {
372 style_mask = NSWindowStyleMask::NSClosableWindowMask
373 | NSWindowStyleMask::NSMiniaturizableWindowMask
374 | NSWindowStyleMask::NSResizableWindowMask
375 | NSWindowStyleMask::NSTitledWindowMask;
376
377 if titlebar.appears_transparent {
378 style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
379 }
380 } else {
381 style_mask = NSWindowStyleMask::NSTitledWindowMask
382 | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
383 }
384
385 let native_window: id = match options.kind {
386 WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
387 WindowKind::PopUp => {
388 style_mask |= NSWindowStyleMaskNonactivatingPanel;
389 msg_send![PANEL_CLASS, alloc]
390 }
391 };
392 let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
393 RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
394 style_mask,
395 NSBackingStoreBuffered,
396 NO,
397 options
398 .screen
399 .and_then(|screen| {
400 Some(screen.as_any().downcast_ref::<Screen>()?.native_screen)
401 })
402 .unwrap_or(nil),
403 );
404 assert!(!native_window.is_null());
405
406 let screen = native_window.screen();
407 match options.bounds {
408 WindowBounds::Maximized => {
409 native_window.setFrame_display_(screen.visibleFrame(), YES);
410 }
411 WindowBounds::Fixed(top_left_bounds) => {
412 let frame = screen.visibleFrame();
413 let bottom_left_bounds = RectF::new(
414 vec2f(
415 top_left_bounds.origin_x(),
416 frame.size.height as f32
417 - top_left_bounds.origin_y()
418 - top_left_bounds.height(),
419 ),
420 top_left_bounds.size(),
421 )
422 .to_ns_rect();
423 native_window.setFrame_display_(
424 native_window.convertRectToScreen_(bottom_left_bounds),
425 YES,
426 );
427 }
428 }
429
430 let native_view: id = msg_send![VIEW_CLASS, alloc];
431 let native_view = NSView::init(native_view);
432 assert!(!native_view.is_null());
433
434 let window = Self(Rc::new(RefCell::new(WindowState {
435 id,
436 native_window,
437 kind: options.kind,
438 event_callback: None,
439 resize_callback: None,
440 should_close_callback: None,
441 close_callback: None,
442 activate_callback: None,
443 fullscreen_callback: None,
444 appearance_changed_callback: None,
445 input_handler: None,
446 pending_key_down: None,
447 performed_key_equivalent: false,
448 synthetic_drag_counter: 0,
449 executor,
450 scene_to_render: Default::default(),
451 renderer: Renderer::new(true, fonts),
452 last_fresh_keydown: None,
453 traffic_light_position: options
454 .titlebar
455 .as_ref()
456 .and_then(|titlebar| titlebar.traffic_light_position),
457 previous_modifiers_changed_event: None,
458 ime_state: ImeState::None,
459 ime_text: None,
460 })));
461
462 (*native_window).set_ivar(
463 WINDOW_STATE_IVAR,
464 Rc::into_raw(window.0.clone()) as *const c_void,
465 );
466 native_window.setDelegate_(native_window);
467 (*native_view).set_ivar(
468 WINDOW_STATE_IVAR,
469 Rc::into_raw(window.0.clone()) as *const c_void,
470 );
471
472 if let Some(title) = options.titlebar.as_ref().and_then(|t| t.title) {
473 native_window.setTitle_(NSString::alloc(nil).init_str(title));
474 }
475
476 native_window.setMovable_(options.is_movable as BOOL);
477
478 if options
479 .titlebar
480 .map_or(true, |titlebar| titlebar.appears_transparent)
481 {
482 native_window.setTitlebarAppearsTransparent_(YES);
483 }
484
485 native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
486 native_view.setWantsBestResolutionOpenGLSurface_(YES);
487
488 // From winit crate: On Mojave, views automatically become layer-backed shortly after
489 // being added to a native_window. Changing the layer-backedness of a view breaks the
490 // association between the view and its associated OpenGL context. To work around this,
491 // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
492 // itself and break the association with its context.
493 native_view.setWantsLayer(YES);
494 let _: () = msg_send![
495 native_view,
496 setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
497 ];
498
499 native_window.setContentView_(native_view.autorelease());
500 native_window.makeFirstResponder_(native_view);
501
502 if options.center {
503 native_window.center();
504 }
505
506 match options.kind {
507 WindowKind::Normal => {
508 native_window.setLevel_(NSNormalWindowLevel);
509 native_window.setAcceptsMouseMovedEvents_(YES);
510 }
511 WindowKind::PopUp => {
512 // Use a tracking area to allow receiving MouseMoved events even when
513 // the window or application aren't active, which is often the case
514 // e.g. for notification windows.
515 let tracking_area: id = msg_send![class!(NSTrackingArea), alloc];
516 let _: () = msg_send![
517 tracking_area,
518 initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.))
519 options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
520 owner: native_view
521 userInfo: nil
522 ];
523 let _: () =
524 msg_send![native_view, addTrackingArea: tracking_area.autorelease()];
525
526 native_window.setLevel_(NSPopUpWindowLevel);
527 let _: () = msg_send![
528 native_window,
529 setAnimationBehavior: NSWindowAnimationBehaviorUtilityWindow
530 ];
531 native_window.setCollectionBehavior_(
532 NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces |
533 NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary
534 );
535 }
536 }
537 if options.focus {
538 native_window.makeKeyAndOrderFront_(nil);
539 } else {
540 native_window.orderFront_(nil);
541 }
542
543 window.0.borrow().move_traffic_light();
544 pool.drain();
545
546 window
547 }
548 }
549
550 pub fn key_window_id() -> Option<usize> {
551 unsafe {
552 let app = NSApplication::sharedApplication(nil);
553 let key_window: id = msg_send![app, keyWindow];
554 if msg_send![key_window, isKindOfClass: WINDOW_CLASS] {
555 let id = get_window_state(&*key_window).borrow().id;
556 Some(id)
557 } else {
558 None
559 }
560 }
561 }
562}
563
564impl Drop for Window {
565 fn drop(&mut self) {
566 let this = self.0.borrow();
567 let window = this.native_window;
568 this.executor
569 .spawn(async move {
570 unsafe {
571 window.close();
572 }
573 })
574 .detach();
575 }
576}
577
578impl platform::Window for Window {
579 fn as_any_mut(&mut self) -> &mut dyn Any {
580 self
581 }
582
583 fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
584 self.0.as_ref().borrow_mut().event_callback = Some(callback);
585 }
586
587 fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
588 self.0.as_ref().borrow_mut().resize_callback = Some(callback);
589 }
590
591 fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
592 self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
593 }
594
595 fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
596 self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
597 }
598
599 fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
600 self.0.as_ref().borrow_mut().close_callback = Some(callback);
601 }
602
603 fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
604 self.0.as_ref().borrow_mut().activate_callback = Some(callback);
605 }
606
607 fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
608 self.0.as_ref().borrow_mut().input_handler = Some(input_handler);
609 }
610
611 fn prompt(
612 &self,
613 level: platform::PromptLevel,
614 msg: &str,
615 answers: &[&str],
616 ) -> oneshot::Receiver<usize> {
617 unsafe {
618 let alert: id = msg_send![class!(NSAlert), alloc];
619 let alert: id = msg_send![alert, init];
620 let alert_style = match level {
621 platform::PromptLevel::Info => 1,
622 platform::PromptLevel::Warning => 0,
623 platform::PromptLevel::Critical => 2,
624 };
625 let _: () = msg_send![alert, setAlertStyle: alert_style];
626 let _: () = msg_send![alert, setMessageText: ns_string(msg)];
627 for (ix, answer) in answers.iter().enumerate() {
628 let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
629 let _: () = msg_send![button, setTag: ix as NSInteger];
630 }
631 let (done_tx, done_rx) = oneshot::channel();
632 let done_tx = Cell::new(Some(done_tx));
633 let block = ConcreteBlock::new(move |answer: NSInteger| {
634 if let Some(mut done_tx) = done_tx.take() {
635 let _ = postage::sink::Sink::try_send(&mut done_tx, answer.try_into().unwrap());
636 }
637 });
638 let block = block.copy();
639 let native_window = self.0.borrow().native_window;
640 self.0
641 .borrow()
642 .executor
643 .spawn(async move {
644 let _: () = msg_send![
645 alert,
646 beginSheetModalForWindow: native_window
647 completionHandler: block
648 ];
649 })
650 .detach();
651
652 done_rx
653 }
654 }
655
656 fn activate(&self) {
657 let window = self.0.borrow().native_window;
658 self.0
659 .borrow()
660 .executor
661 .spawn(async move {
662 unsafe {
663 let _: () = msg_send![window, makeKeyAndOrderFront: nil];
664 }
665 })
666 .detach();
667 }
668
669 fn set_title(&mut self, title: &str) {
670 unsafe {
671 let app = NSApplication::sharedApplication(nil);
672 let window = self.0.borrow().native_window;
673 let title = ns_string(title);
674 msg_send![app, changeWindowsItem:window title:title filename:false]
675 }
676 }
677
678 fn set_edited(&mut self, edited: bool) {
679 unsafe {
680 let window = self.0.borrow().native_window;
681 msg_send![window, setDocumentEdited: edited as BOOL]
682 }
683
684 // Changing the document edited state resets the traffic light position,
685 // so we have to move it again.
686 self.0.borrow().move_traffic_light();
687 }
688
689 fn show_character_palette(&self) {
690 unsafe {
691 let app = NSApplication::sharedApplication(nil);
692 let window = self.0.borrow().native_window;
693 let _: () = msg_send![app, orderFrontCharacterPalette: window];
694 }
695 }
696
697 fn minimize(&self) {
698 let window = self.0.borrow().native_window;
699 unsafe {
700 window.miniaturize_(nil);
701 }
702 }
703
704 fn zoom(&self) {
705 let this = self.0.borrow();
706 let window = this.native_window;
707 this.executor
708 .spawn(async move {
709 unsafe {
710 window.zoom_(nil);
711 }
712 })
713 .detach();
714 }
715
716 fn toggle_full_screen(&self) {
717 let this = self.0.borrow();
718 let window = this.native_window;
719 this.executor
720 .spawn(async move {
721 unsafe {
722 window.toggleFullScreen_(nil);
723 }
724 })
725 .detach();
726 }
727
728 fn bounds(&self) -> RectF {
729 self.0.as_ref().borrow().bounds()
730 }
731
732 fn content_size(&self) -> Vector2F {
733 self.0.as_ref().borrow().content_size()
734 }
735
736 fn scale_factor(&self) -> f32 {
737 self.0.as_ref().borrow().scale_factor()
738 }
739
740 fn present_scene(&mut self, scene: Scene) {
741 self.0.as_ref().borrow_mut().present_scene(scene);
742 }
743
744 fn titlebar_height(&self) -> f32 {
745 self.0.as_ref().borrow().titlebar_height()
746 }
747
748 fn appearance(&self) -> crate::Appearance {
749 unsafe {
750 let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
751 crate::Appearance::from_native(appearance)
752 }
753 }
754
755 fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
756 self.0.borrow_mut().appearance_changed_callback = Some(callback);
757 }
758}
759
760impl WindowState {
761 fn move_traffic_light(&self) {
762 if let Some(traffic_light_position) = self.traffic_light_position {
763 let titlebar_height = self.titlebar_height();
764
765 unsafe {
766 let close_button: id = msg_send![
767 self.native_window,
768 standardWindowButton: NSWindowButton::NSWindowCloseButton
769 ];
770 let min_button: id = msg_send![
771 self.native_window,
772 standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
773 ];
774 let zoom_button: id = msg_send![
775 self.native_window,
776 standardWindowButton: NSWindowButton::NSWindowZoomButton
777 ];
778
779 let mut close_button_frame: CGRect = msg_send![close_button, frame];
780 let mut min_button_frame: CGRect = msg_send![min_button, frame];
781 let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
782 let mut origin = vec2f(
783 traffic_light_position.x(),
784 titlebar_height
785 - traffic_light_position.y()
786 - close_button_frame.size.height as f32,
787 );
788 let button_spacing =
789 (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
790
791 close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
792 let _: () = msg_send![close_button, setFrame: close_button_frame];
793 origin.set_x(origin.x() + button_spacing);
794
795 min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
796 let _: () = msg_send![min_button, setFrame: min_button_frame];
797 origin.set_x(origin.x() + button_spacing);
798
799 zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
800 let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
801 }
802 }
803 }
804
805 fn bounds(&self) -> RectF {
806 unsafe {
807 let screen_frame = self.native_window.screen().visibleFrame();
808 let window_frame = NSWindow::frame(self.native_window);
809 let origin = vec2f(
810 window_frame.origin.x as f32,
811 (window_frame.origin.y - screen_frame.size.height - window_frame.size.height)
812 as f32,
813 );
814 let size = vec2f(
815 window_frame.size.width as f32,
816 window_frame.size.height as f32,
817 );
818 RectF::new(origin, size)
819 }
820 }
821
822 fn content_size(&self) -> Vector2F {
823 let NSSize { width, height, .. } =
824 unsafe { NSView::frame(self.native_window.contentView()) }.size;
825 vec2f(width as f32, height as f32)
826 }
827
828 fn scale_factor(&self) -> f32 {
829 get_scale_factor(self.native_window)
830 }
831
832 fn titlebar_height(&self) -> f32 {
833 unsafe {
834 let frame = NSWindow::frame(self.native_window);
835 let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
836 (frame.size.height - content_layout_rect.size.height) as f32
837 }
838 }
839
840 fn present_scene(&mut self, scene: Scene) {
841 self.scene_to_render = Some(scene);
842 unsafe {
843 let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
844 }
845 }
846}
847
848fn get_scale_factor(native_window: id) -> f32 {
849 unsafe {
850 let screen: id = msg_send![native_window, screen];
851 NSScreen::backingScaleFactor(screen) as f32
852 }
853}
854
855unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
856 let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
857 let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
858 let rc2 = rc1.clone();
859 mem::forget(rc1);
860 rc2
861}
862
863unsafe fn drop_window_state(object: &Object) {
864 let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
865 Rc::from_raw(raw as *mut RefCell<WindowState>);
866}
867
868extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
869 YES
870}
871
872extern "C" fn dealloc_window(this: &Object, _: Sel) {
873 unsafe {
874 drop_window_state(this);
875 let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
876 }
877}
878
879extern "C" fn dealloc_view(this: &Object, _: Sel) {
880 unsafe {
881 drop_window_state(this);
882 let _: () = msg_send![super(this, class!(NSView)), dealloc];
883 }
884}
885
886extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
887 handle_key_event(this, native_event, true)
888}
889
890extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
891 handle_key_event(this, native_event, false);
892}
893
894extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
895 let window_state = unsafe { get_window_state(this) };
896 let mut window_state_borrow = window_state.as_ref().borrow_mut();
897
898 let window_height = window_state_borrow.content_size().y();
899 let event = unsafe { Event::from_native(native_event, Some(window_height)) };
900
901 if let Some(event) = event {
902 if key_equivalent {
903 window_state_borrow.performed_key_equivalent = true;
904 } else if window_state_borrow.performed_key_equivalent {
905 return NO;
906 }
907
908 let function_is_held;
909 window_state_borrow.pending_key_down = match event {
910 Event::KeyDown(event) => {
911 let keydown = event.keystroke.clone();
912 // Ignore events from held-down keys after some of the initially-pressed keys
913 // were released.
914 if event.is_held {
915 if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
916 return YES;
917 }
918 } else {
919 window_state_borrow.last_fresh_keydown = Some(keydown);
920 }
921 function_is_held = event.keystroke.function;
922 Some((event, None))
923 }
924
925 _ => return NO,
926 };
927
928 drop(window_state_borrow);
929
930 if !function_is_held {
931 unsafe {
932 let input_context: id = msg_send![this, inputContext];
933 let _: BOOL = msg_send![input_context, handleEvent: native_event];
934 }
935 }
936
937 let mut handled = false;
938 let mut window_state_borrow = window_state.borrow_mut();
939 let ime_text = window_state_borrow.ime_text.clone();
940 if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
941 let is_held = event.is_held;
942 if let Some(mut callback) = window_state_borrow.event_callback.take() {
943 drop(window_state_borrow);
944
945 let is_composing =
946 with_input_handler(this, |input_handler| input_handler.marked_text_range())
947 .flatten()
948 .is_some();
949 if !is_composing {
950 handled = callback(Event::KeyDown(event));
951 }
952
953 if !handled {
954 if let Some(insert) = insert_text {
955 handled = true;
956 with_input_handler(this, |input_handler| {
957 input_handler
958 .replace_text_in_range(insert.replacement_range, &insert.text)
959 });
960 } else if !is_composing && is_held {
961 if let Some(last_insert_text) = ime_text {
962 //MacOS IME is a bit funky, and even when you've told it there's nothing to
963 //inter it will still swallow certain keys (e.g. 'f', 'j') and not others
964 //(e.g. 'n'). This is a problem for certain kinds of views, like the terminal
965 with_input_handler(this, |input_handler| {
966 if input_handler.selected_text_range().is_none() {
967 handled = true;
968 input_handler.replace_text_in_range(None, &last_insert_text)
969 }
970 });
971 }
972 }
973 }
974
975 window_state.borrow_mut().event_callback = Some(callback);
976 }
977 } else {
978 handled = true;
979 }
980
981 handled as BOOL
982 } else {
983 NO
984 }
985}
986
987extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
988 let window_state = unsafe { get_window_state(this) };
989 let weak_window_state = Rc::downgrade(&window_state);
990 let mut window_state_borrow = window_state.as_ref().borrow_mut();
991 let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
992
993 let window_height = window_state_borrow.content_size().y();
994 let event = unsafe { Event::from_native(native_event, Some(window_height)) };
995 if let Some(event) = event {
996 match &event {
997 Event::MouseMoved(
998 event @ MouseMovedEvent {
999 pressed_button: Some(_),
1000 ..
1001 },
1002 ) => {
1003 window_state_borrow.synthetic_drag_counter += 1;
1004 window_state_borrow
1005 .executor
1006 .spawn(synthetic_drag(
1007 weak_window_state,
1008 window_state_borrow.synthetic_drag_counter,
1009 *event,
1010 ))
1011 .detach();
1012 }
1013
1014 Event::MouseMoved(_)
1015 if !(is_active || window_state_borrow.kind == WindowKind::PopUp) =>
1016 {
1017 return
1018 }
1019
1020 Event::MouseUp(MouseButtonEvent {
1021 button: MouseButton::Left,
1022 ..
1023 }) => {
1024 window_state_borrow.synthetic_drag_counter += 1;
1025 }
1026
1027 Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1028 // Only raise modifiers changed event when they have actually changed
1029 if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
1030 modifiers: prev_modifiers,
1031 })) = &window_state_borrow.previous_modifiers_changed_event
1032 {
1033 if prev_modifiers == modifiers {
1034 return;
1035 }
1036 }
1037
1038 window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
1039 }
1040
1041 _ => {}
1042 }
1043
1044 if let Some(mut callback) = window_state_borrow.event_callback.take() {
1045 drop(window_state_borrow);
1046 callback(event);
1047 window_state.borrow_mut().event_callback = Some(callback);
1048 }
1049 }
1050}
1051
1052// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
1053// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
1054extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
1055 let window_state = unsafe { get_window_state(this) };
1056 let mut window_state_borrow = window_state.as_ref().borrow_mut();
1057
1058 let keystroke = Keystroke {
1059 cmd: true,
1060 ctrl: false,
1061 alt: false,
1062 shift: false,
1063 function: false,
1064 key: ".".into(),
1065 };
1066 let event = Event::KeyDown(KeyDownEvent {
1067 keystroke: keystroke.clone(),
1068 is_held: false,
1069 });
1070
1071 window_state_borrow.last_fresh_keydown = Some(keystroke);
1072 if let Some(mut callback) = window_state_borrow.event_callback.take() {
1073 drop(window_state_borrow);
1074 callback(event);
1075 window_state.borrow_mut().event_callback = Some(callback);
1076 }
1077}
1078
1079extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
1080 unsafe {
1081 let _: () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
1082 get_window_state(this).borrow_mut().performed_key_equivalent = false;
1083 }
1084}
1085
1086extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1087 let window_state = unsafe { get_window_state(this) };
1088 window_state.as_ref().borrow().move_traffic_light();
1089}
1090
1091extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
1092 window_fullscreen_changed(this, true);
1093}
1094
1095extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
1096 window_fullscreen_changed(this, false);
1097}
1098
1099fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
1100 let window_state = unsafe { get_window_state(this) };
1101 let mut window_state_borrow = window_state.as_ref().borrow_mut();
1102 if let Some(mut callback) = window_state_borrow.fullscreen_callback.take() {
1103 drop(window_state_borrow);
1104 callback(is_fullscreen);
1105 window_state.borrow_mut().fullscreen_callback = Some(callback);
1106 }
1107}
1108
1109extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1110 let window_state = unsafe { get_window_state(this) };
1111 let window_state_borrow = window_state.borrow();
1112 let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
1113
1114 // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1115 // `windowDidBecomeKey` message to the previous key window even though that window
1116 // isn't actually key. This causes a bug if the application is later activated while
1117 // the pop-up is still open, making it impossible to activate the previous key window
1118 // even if the pop-up gets closed. The only way to activate it again is to de-activate
1119 // the app and re-activate it, which is a pretty bad UX.
1120 // The following code detects the spurious event and invokes `resignKeyWindow`:
1121 // in theory, we're not supposed to invoke this method manually but it balances out
1122 // the spurious `becomeKeyWindow` event and helps us work around that bug.
1123 if selector == sel!(windowDidBecomeKey:) {
1124 if !is_active {
1125 unsafe {
1126 let _: () = msg_send![window_state_borrow.native_window, resignKeyWindow];
1127 return;
1128 }
1129 }
1130 }
1131
1132 let executor = window_state_borrow.executor.clone();
1133 drop(window_state_borrow);
1134 executor
1135 .spawn(async move {
1136 let mut window_state_borrow = window_state.as_ref().borrow_mut();
1137 if let Some(mut callback) = window_state_borrow.activate_callback.take() {
1138 drop(window_state_borrow);
1139 callback(is_active);
1140 window_state.borrow_mut().activate_callback = Some(callback);
1141 };
1142 })
1143 .detach();
1144}
1145
1146extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1147 let window_state = unsafe { get_window_state(this) };
1148 let mut window_state_borrow = window_state.as_ref().borrow_mut();
1149 if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
1150 drop(window_state_borrow);
1151 let should_close = callback();
1152 window_state.borrow_mut().should_close_callback = Some(callback);
1153 should_close as BOOL
1154 } else {
1155 YES
1156 }
1157}
1158
1159extern "C" fn close_window(this: &Object, _: Sel) {
1160 unsafe {
1161 let close_callback = {
1162 let window_state = get_window_state(this);
1163 window_state
1164 .as_ref()
1165 .try_borrow_mut()
1166 .ok()
1167 .and_then(|mut window_state| window_state.close_callback.take())
1168 };
1169
1170 if let Some(callback) = close_callback {
1171 callback();
1172 }
1173
1174 let _: () = msg_send![super(this, class!(NSWindow)), close];
1175 }
1176}
1177
1178extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1179 let window_state = unsafe { get_window_state(this) };
1180 let window_state = window_state.as_ref().borrow();
1181 window_state.renderer.layer().as_ptr() as id
1182}
1183
1184extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1185 let window_state = unsafe { get_window_state(this) };
1186 let mut window_state_borrow = window_state.as_ref().borrow_mut();
1187
1188 unsafe {
1189 let scale_factor = window_state_borrow.scale_factor() as f64;
1190 let size = window_state_borrow.content_size();
1191 let drawable_size: NSSize = NSSize {
1192 width: size.x() as f64 * scale_factor,
1193 height: size.y() as f64 * scale_factor,
1194 };
1195
1196 let _: () = msg_send![
1197 window_state_borrow.renderer.layer(),
1198 setContentsScale: scale_factor
1199 ];
1200 let _: () = msg_send![
1201 window_state_borrow.renderer.layer(),
1202 setDrawableSize: drawable_size
1203 ];
1204 }
1205
1206 if let Some(mut callback) = window_state_borrow.resize_callback.take() {
1207 drop(window_state_borrow);
1208 callback();
1209 window_state.as_ref().borrow_mut().resize_callback = Some(callback);
1210 };
1211}
1212
1213extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1214 let window_state = unsafe { get_window_state(this) };
1215 let window_state_borrow = window_state.as_ref().borrow();
1216
1217 if window_state_borrow.content_size() == vec2f(size.width as f32, size.height as f32) {
1218 return;
1219 }
1220
1221 unsafe {
1222 let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1223 }
1224
1225 let scale_factor = window_state_borrow.scale_factor() as f64;
1226 let drawable_size: NSSize = NSSize {
1227 width: size.width * scale_factor,
1228 height: size.height * scale_factor,
1229 };
1230
1231 unsafe {
1232 let _: () = msg_send![
1233 window_state_borrow.renderer.layer(),
1234 setDrawableSize: drawable_size
1235 ];
1236 }
1237
1238 drop(window_state_borrow);
1239 let mut window_state_borrow = window_state.borrow_mut();
1240 if let Some(mut callback) = window_state_borrow.resize_callback.take() {
1241 drop(window_state_borrow);
1242 callback();
1243 window_state.borrow_mut().resize_callback = Some(callback);
1244 };
1245}
1246
1247extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
1248 unsafe {
1249 let window_state = get_window_state(this);
1250 let mut window_state = window_state.as_ref().borrow_mut();
1251 if let Some(scene) = window_state.scene_to_render.take() {
1252 window_state.renderer.render(&scene);
1253 };
1254 }
1255}
1256
1257extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1258 unsafe { msg_send![class!(NSArray), array] }
1259}
1260
1261extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1262 with_input_handler(this, |input_handler| input_handler.marked_text_range())
1263 .flatten()
1264 .is_some() as BOOL
1265}
1266
1267extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1268 with_input_handler(this, |input_handler| input_handler.marked_text_range())
1269 .flatten()
1270 .map_or(NSRange::invalid(), |range| range.into())
1271}
1272
1273extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1274 with_input_handler(this, |input_handler| input_handler.selected_text_range())
1275 .flatten()
1276 .map_or(NSRange::invalid(), |range| range.into())
1277}
1278
1279extern "C" fn first_rect_for_character_range(
1280 this: &Object,
1281 _: Sel,
1282 range: NSRange,
1283 _: id,
1284) -> NSRect {
1285 let frame = unsafe {
1286 let window = get_window_state(this).borrow().native_window;
1287 NSView::frame(window)
1288 };
1289 with_input_handler(this, |input_handler| {
1290 input_handler.rect_for_range(range.to_range()?)
1291 })
1292 .flatten()
1293 .map_or(
1294 NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1295 |rect| {
1296 NSRect::new(
1297 NSPoint::new(
1298 frame.origin.x + rect.origin_x() as f64,
1299 frame.origin.y + frame.size.height - rect.origin_y() as f64,
1300 ),
1301 NSSize::new(rect.width() as f64, rect.height() as f64),
1302 )
1303 },
1304 )
1305}
1306
1307extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1308 unsafe {
1309 let window_state = get_window_state(this);
1310 let mut window_state_borrow = window_state.borrow_mut();
1311 let pending_key_down = window_state_borrow.pending_key_down.take();
1312 drop(window_state_borrow);
1313
1314 let is_attributed_string: BOOL =
1315 msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1316 let text: id = if is_attributed_string == YES {
1317 msg_send![text, string]
1318 } else {
1319 text
1320 };
1321 let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1322 .to_str()
1323 .unwrap();
1324 let replacement_range = replacement_range.to_range();
1325
1326 window_state.borrow_mut().ime_text = Some(text.to_string());
1327 window_state.borrow_mut().ime_state = ImeState::Acted;
1328
1329 let is_composing =
1330 with_input_handler(this, |input_handler| input_handler.marked_text_range())
1331 .flatten()
1332 .is_some();
1333
1334 if is_composing || text.chars().count() > 1 || pending_key_down.is_none() {
1335 with_input_handler(this, |input_handler| {
1336 input_handler.replace_text_in_range(replacement_range, text)
1337 });
1338 } else {
1339 let mut pending_key_down = pending_key_down.unwrap();
1340 pending_key_down.1 = Some(InsertText {
1341 replacement_range,
1342 text: text.to_string(),
1343 });
1344 window_state.borrow_mut().pending_key_down = Some(pending_key_down);
1345 }
1346 }
1347}
1348
1349extern "C" fn set_marked_text(
1350 this: &Object,
1351 _: Sel,
1352 text: id,
1353 selected_range: NSRange,
1354 replacement_range: NSRange,
1355) {
1356 unsafe {
1357 let window_state = get_window_state(this);
1358 window_state.borrow_mut().pending_key_down.take();
1359
1360 let is_attributed_string: BOOL =
1361 msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1362 let text: id = if is_attributed_string == YES {
1363 msg_send![text, string]
1364 } else {
1365 text
1366 };
1367 let selected_range = selected_range.to_range();
1368 let replacement_range = replacement_range.to_range();
1369 let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1370 .to_str()
1371 .unwrap();
1372
1373 window_state.borrow_mut().ime_state = ImeState::Acted;
1374 window_state.borrow_mut().ime_text = Some(text.to_string());
1375
1376 with_input_handler(this, |input_handler| {
1377 input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
1378 });
1379 }
1380}
1381
1382extern "C" fn unmark_text(this: &Object, _: Sel) {
1383 unsafe {
1384 let state = get_window_state(this);
1385 let mut borrow = state.borrow_mut();
1386 borrow.ime_state = ImeState::Acted;
1387 borrow.ime_text.take();
1388 }
1389
1390 with_input_handler(this, |input_handler| input_handler.unmark_text());
1391}
1392
1393extern "C" fn attributed_substring_for_proposed_range(
1394 this: &Object,
1395 _: Sel,
1396 range: NSRange,
1397 _actual_range: *mut c_void,
1398) -> id {
1399 with_input_handler(this, |input_handler| {
1400 let range = range.to_range()?;
1401 if range.is_empty() {
1402 return None;
1403 }
1404
1405 let selected_text = input_handler.text_for_range(range)?;
1406 unsafe {
1407 let string: id = msg_send![class!(NSAttributedString), alloc];
1408 let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1409 Some(string)
1410 }
1411 })
1412 .flatten()
1413 .unwrap_or(nil)
1414}
1415
1416extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
1417 unsafe {
1418 let state = get_window_state(this);
1419 let mut borrow = state.borrow_mut();
1420 borrow.ime_state = ImeState::Continue;
1421 borrow.ime_text.take();
1422 }
1423}
1424
1425extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1426 unsafe {
1427 let state = get_window_state(this);
1428 let mut state_borrow = state.as_ref().borrow_mut();
1429 if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {
1430 drop(state_borrow);
1431 callback();
1432 state.borrow_mut().appearance_changed_callback = Some(callback);
1433 }
1434 }
1435}
1436
1437extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
1438 unsafe {
1439 let state = get_window_state(this);
1440 let state_borrow = state.as_ref().borrow();
1441 return if state_borrow.kind == WindowKind::PopUp {
1442 YES
1443 } else {
1444 NO
1445 };
1446 }
1447}
1448
1449async fn synthetic_drag(
1450 window_state: Weak<RefCell<WindowState>>,
1451 drag_id: usize,
1452 event: MouseMovedEvent,
1453) {
1454 loop {
1455 Timer::after(Duration::from_millis(16)).await;
1456 if let Some(window_state) = window_state.upgrade() {
1457 let mut window_state_borrow = window_state.borrow_mut();
1458 if window_state_borrow.synthetic_drag_counter == drag_id {
1459 if let Some(mut callback) = window_state_borrow.event_callback.take() {
1460 drop(window_state_borrow);
1461 callback(Event::MouseMoved(event));
1462 window_state.borrow_mut().event_callback = Some(callback);
1463 }
1464 } else {
1465 break;
1466 }
1467 }
1468 }
1469}
1470
1471unsafe fn ns_string(string: &str) -> id {
1472 NSString::alloc(nil).init_str(string).autorelease()
1473}
1474
1475fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
1476where
1477 F: FnOnce(&mut dyn InputHandler) -> R,
1478{
1479 let window_state = unsafe { get_window_state(window) };
1480 let mut window_state_borrow = window_state.as_ref().borrow_mut();
1481 if let Some(mut input_handler) = window_state_borrow.input_handler.take() {
1482 drop(window_state_borrow);
1483 let result = f(input_handler.as_mut());
1484 window_state.borrow_mut().input_handler = Some(input_handler);
1485 Some(result)
1486 } else {
1487 None
1488 }
1489}