Echo Writes Code

macos.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use objc2::{ DefinedClass, MainThreadOnly, define_class, msg_send };
use objc2::rc::{ Retained };
use objc2::runtime::{ ProtocolObject };

use objc2_foundation::{
  MainThreadMarker,
  NSNotification,
  NSObject,
  NSObjectProtocol,
  NSPoint,
  NSRect,
  NSSize,
  ns_string,
};

use objc2_app_kit::{
  NSApplication,
  NSApplicationDelegate,
  NSApplicationActivationPolicy,
  NSBackingStoreType,
  NSWindow,
  NSWindowDelegate,
  NSWindowStyleMask,
};

use std::cell::{ OnceCell };

#[derive(Debug, Default)]
struct CrucibleDelegateIvars {
  window: OnceCell<Retained<NSWindow>>,
}

define_class!{
  #[unsafe(super = NSObject)]
  #[thread_kind = MainThreadOnly]
  #[ivars = CrucibleDelegateIvars]
  struct CrucibleDelegate;

  unsafe impl NSObjectProtocol for CrucibleDelegate {}

  unsafe impl NSApplicationDelegate for CrucibleDelegate {
    #[unsafe(method(applicationDidFinishLaunching:))]
    fn did_finish_launching(&self, notification: &NSNotification) {
      let mtm = self.mtm();

      let application = unsafe {
        notification.object()
          .expect("applicationDidFinishLaunching notification should have an object")
          .downcast::<NSApplication>()
          .expect("applicationDidFinishLaunching notification object should be an NSApplication")
      };

      let window = unsafe {
        let content_rect = NSRect::new(
          NSPoint::new(0.0, 0.0),
          NSSize::new(1280.0, 720.0)
        );

        let style_mask = NSWindowStyleMask::Titled
          | NSWindowStyleMask::Closable
          | NSWindowStyleMask::Miniaturizable
          | NSWindowStyleMask::Resizable;

        NSWindow::initWithContentRect_styleMask_backing_defer(
          NSWindow::alloc(mtm),
          content_rect,
          style_mask,
          NSBackingStoreType::Buffered,
          false
        )
      };

      unsafe {
        window.setReleasedWhenClosed(false)
      };

      window.setTitle(ns_string!("Crucible Application"));
      window.center();
      window.setDelegate(Some(ProtocolObject::from_ref(self)));
      window.makeKeyAndOrderFront(None);

      self
        .ivars()
        .window
        .set(window)
        .expect("Should have been able to set window as an ivar");

      application.setActivationPolicy(NSApplicationActivationPolicy::Regular);

      #[allow(deprecated)]
      application.activateIgnoringOtherApps(true);
    }
  }

  unsafe impl NSWindowDelegate for CrucibleDelegate {
    #[unsafe(method(windowWillClose:))]
    fn window_will_close(&self, _notification: &NSNotification) {
      unsafe {
        NSApplication::sharedApplication(self.mtm())
          .terminate(None)
      };
    }
  }
}

impl CrucibleDelegate {
  pub fn new(mtm: MainThreadMarker) -> Retained<CrucibleDelegate> {
    let this = CrucibleDelegate::alloc(mtm)
      .set_ivars(CrucibleDelegateIvars::default());

    unsafe {
      msg_send![super(this), init]
    }
  }
}

pub fn run() {
  let mtm = MainThreadMarker::new()
    .expect("Must be called from the main thread");

  let application = NSApplication::sharedApplication(mtm);
  let delegate = CrucibleDelegate::new(mtm);
  application.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));

  application.run();
}