window.rs

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