app.rs

  1use super::{BoolExt as _, Dispatcher, FontSystem, Window};
  2use crate::{executor, platform};
  3use anyhow::Result;
  4use cocoa::{
  5    appkit::{NSApplication, NSModalResponse, NSOpenPanel, NSPasteboard, NSPasteboardTypeString},
  6    base::nil,
  7    foundation::{NSArray, NSData, NSString, NSURL},
  8};
  9use objc::{msg_send, sel, sel_impl};
 10use std::{ffi::c_void, path::PathBuf, rc::Rc, sync::Arc};
 11
 12pub struct App {
 13    dispatcher: Arc<Dispatcher>,
 14    fonts: Arc<FontSystem>,
 15}
 16
 17impl App {
 18    pub fn new() -> Self {
 19        Self {
 20            dispatcher: Arc::new(Dispatcher),
 21            fonts: Arc::new(FontSystem::new()),
 22        }
 23    }
 24}
 25
 26impl platform::App for App {
 27    fn dispatcher(&self) -> Arc<dyn platform::Dispatcher> {
 28        self.dispatcher.clone()
 29    }
 30
 31    fn activate(&self, ignoring_other_apps: bool) {
 32        unsafe {
 33            let app = NSApplication::sharedApplication(nil);
 34            app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc());
 35        }
 36    }
 37
 38    fn open_window(
 39        &self,
 40        options: platform::WindowOptions,
 41        executor: Rc<executor::Foreground>,
 42    ) -> Result<Box<dyn platform::Window>> {
 43        Ok(Box::new(Window::open(options, executor, self.fonts())?))
 44    }
 45
 46    fn prompt_for_paths(
 47        &self,
 48        options: platform::PathPromptOptions,
 49    ) -> Option<Vec<std::path::PathBuf>> {
 50        unsafe {
 51            let panel = NSOpenPanel::openPanel(nil);
 52            panel.setCanChooseDirectories_(options.directories.to_objc());
 53            panel.setCanChooseFiles_(options.files.to_objc());
 54            panel.setAllowsMultipleSelection_(options.multiple.to_objc());
 55            panel.setResolvesAliases_(false.to_objc());
 56            let response = panel.runModal();
 57            if response == NSModalResponse::NSModalResponseOk {
 58                let mut result = Vec::new();
 59                let urls = panel.URLs();
 60                for i in 0..urls.count() {
 61                    let url = urls.objectAtIndex(i);
 62                    let string = url.absoluteString();
 63                    let string = std::ffi::CStr::from_ptr(string.UTF8String())
 64                        .to_string_lossy()
 65                        .to_string();
 66                    if let Some(path) = string.strip_prefix("file://") {
 67                        result.push(PathBuf::from(path));
 68                    }
 69                }
 70                Some(result)
 71            } else {
 72                None
 73            }
 74        }
 75    }
 76
 77    fn fonts(&self) -> Arc<dyn platform::FontSystem> {
 78        self.fonts.clone()
 79    }
 80
 81    fn quit(&self) {
 82        unsafe {
 83            let app = NSApplication::sharedApplication(nil);
 84            let _: () = msg_send![app, terminate: nil];
 85        }
 86    }
 87
 88    fn copy(&self, text: &str) {
 89        unsafe {
 90            let data = NSData::dataWithBytes_length_(
 91                nil,
 92                text.as_ptr() as *const c_void,
 93                text.len() as u64,
 94            );
 95            let pasteboard = NSPasteboard::generalPasteboard(nil);
 96            pasteboard.clearContents();
 97            pasteboard.setData_forType(data, NSPasteboardTypeString);
 98        }
 99    }
100}