window.rs

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