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