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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! `DarwinWKApp` configures the `NSApplication` and opens a `NSWindow`.
use super::dwk_webview::*;

use cocoa::appkit::{
    NSApp, NSApplication, NSApplicationActivateIgnoringOtherApps,
    NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSMenu, NSMenuItem,
    NSRunningApplication, NSWindow, NSWindowStyleMask,
};
use cocoa::base::{id, nil, selector, NO};
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSProcessInfo, NSRect, NSSize, NSString};

/// Wraps an NSApplication instance with a main window that contains WebView.
///
/// See `DarwinWKWebView` as well.
///
/// # Example
///
/// ```no_run
/// use darwin_webkit::helpers::dwk_app::DarwinWKApp;
/// use std::rc::Rc;
///
/// unsafe {
///     let app = DarwinWKApp::new("Host an app");
///     let webview = Rc::new(app.create_webview());
///
///     // add handlers, load HTML, etc...
///
///     app.set_webview(&webview);
///     app.run();
/// }
/// ```
pub struct DarwinWKApp {
    /// The NSApplication instance
    nsapp: id,
    /// The NSWindow instance
    main_window: id,
}

impl DarwinWKApp {
    /// Create an app with the given window title
    ///
    /// # Safety
    /// All the FFI functions are unsafe.
    pub unsafe fn new(windowTitle: &str) -> DarwinWKApp {
        let _pool = NSAutoreleasePool::new(nil);

        let app = NSApp();

        app.setActivationPolicy_(NSApplicationActivationPolicyRegular);

        // create Menu Bar
        let menubar = NSMenu::new(nil).autorelease();
        let app_menu_item = NSMenuItem::new(nil).autorelease();
        menubar.addItem_(app_menu_item);
        app.setMainMenu_(menubar);

        // create Application menu
        let app_menu = NSMenu::new(nil).autorelease();
        let quit_prefix = NSString::alloc(nil).init_str("Quit ");
        let quit_title =
            quit_prefix.stringByAppendingString_(NSProcessInfo::processInfo(nil).processName());
        let quit_action = selector("terminate:");
        let quit_key = NSString::alloc(nil).init_str("q");
        let quit_item = NSMenuItem::alloc(nil)
            .initWithTitle_action_keyEquivalent_(quit_title, quit_action, quit_key)
            .autorelease();
        app_menu.addItem_(quit_item);
        app_menu_item.setSubmenu_(app_menu);

        // create Window
        let styleMask = NSWindowStyleMask::NSTitledWindowMask
            | NSWindowStyleMask::NSClosableWindowMask
            | NSWindowStyleMask::NSResizableWindowMask;

        let window = NSWindow::alloc(nil)
            .initWithContentRect_styleMask_backing_defer_(
                NSRect::new(NSPoint::new(0., 0.), NSSize::new(800., 800.)),
                styleMask,
                NSBackingStoreBuffered,
                NO,
            )
            .autorelease();
        window.cascadeTopLeftFromPoint_(NSPoint::new(20., 20.));
        window.center();

        let title = NSString::alloc(nil).init_str(windowTitle);
        window.setTitle_(title);
        window.makeKeyAndOrderFront_(nil);

        DarwinWKApp {
            nsapp: app,
            main_window: window,
        }
    }

    /// Get the NSApplication handle
    pub fn get_app_native_handle(&self) -> id {
        self.nsapp
    }

    /// Get the NSWindow handle
    pub fn get_window_native_handle(&self) -> id {
        self.main_window
    }

    /// Start the NSApplication and activate it ignoring other apps so it comes to front.
    ///
    /// # Safety
    /// All the FFI functions are unsafe.
    pub unsafe fn run(&self) {
        let current_app = NSRunningApplication::currentApplication(nil);
        current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
        self.nsapp.run();
    }

    /// Stop the NSApplication run loop.
    ///
    /// # Safety
    /// All the FFI functions are unsafe.
    pub unsafe fn stop(&self) {
        msg_send![self.nsapp, stop: nil]
    }

    /// Create a webview that has this app window's frame.
    ///
    /// # Safety
    /// All the FFI functions are unsafe.
    pub unsafe fn create_webview(&self) -> DarwinWKWebView {
        let frame = NSWindow::frame(self.main_window);
        DarwinWKWebView::new(frame)
    }

    /// Set the content view of the main window to a certain webview
    ///
    /// # Safety
    /// All the FFI functions are unsafe.
    pub unsafe fn set_webview<'a>(&'a self, webview: &'a DarwinWKWebView) {
        self.main_window
            .setContentView_(webview.get_native_handle());
    }
}

unsafe impl Send for DarwinWKApp {}
unsafe impl Sync for DarwinWKApp {}