From c7ed95ea8bb277f5475dba5e634d505f1c453e69 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 23 Apr 2024 18:55:13 +0200 Subject: [PATCH 1/2] macOS: Use `objc2` crates These replace the `objc` family of crates, and provide a bunch of improvements on top of it. Improvements relevant for `rfd`: - No longer need to call `msg_send!`, (almost) all methods have automatically generated bindings in `objc2-foundation`. - `MainThreadMarker`, allows marking explicitly the functions that must run on the main thread. - Blocks in `block2` have correct memory management, you no longer need to call `mem::forget`. - AppKit bindings are provided in `objc2-app-kit`, you no longer need to define `NSApplication`, `NSAlertStyle` and such manually. To do this change, I had to change how `AsModal` works, as it was previously relying on the fact that `NSAlert` and `NSSavePanel` just so happened to have mostly compatible methods; now we ensure statically that the correct method is called. --- CHANGELOG.md | 3 + Cargo.lock | 88 ++++--- Cargo.toml | 29 ++- src/backend/macos.rs | 5 - src/backend/macos/file_dialog.rs | 83 ++++--- src/backend/macos/file_dialog/panel_ffi.rs | 276 +++++++++------------ src/backend/macos/message_dialog.rs | 154 +++++------- src/backend/macos/modal_future.rs | 85 ++++--- src/backend/macos/utils.rs | 56 ++--- src/backend/macos/utils/application.rs | 58 ----- src/backend/macos/utils/focus_manager.rs | 13 +- src/backend/macos/utils/policy_manager.rs | 43 ++-- src/backend/macos/utils/url.rs | 23 -- src/backend/macos/utils/window.rs | 30 --- 14 files changed, 402 insertions(+), 544 deletions(-) delete mode 100644 src/backend/macos/utils/application.rs delete mode 100644 src/backend/macos/utils/url.rs delete mode 100644 src/backend/macos/utils/window.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7861ef1..1ff5367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## Unreleased +- Move from `objc` crates to `objc2` crates. + ## 0.14.0 - i18n for GTK and XDG Portal - Use XDG Portal as default diff --git a/Cargo.lock b/Cargo.lock index 7ab5ea5..feba393 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,12 +254,6 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -269,6 +263,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ff7d91d3c1d568065b06c899777d1e48dcf76103a672a0adbc238a7f247f1e" +dependencies = [ + "objc2", +] + [[package]] name = "blocking" version = "1.5.1" @@ -778,15 +781,6 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.7.1" @@ -836,32 +830,59 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da284c198fb9b7b0603f8635185e85fbd5b64ee154b1ed406d489077de2d6d60" + +[[package]] +name = "objc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b25e1034d0e636cd84707ccdaa9f81243d399196b8a773946dcffec0401659" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "fb79768a710a9a1798848179edb186d1af7e8a8679f369e4b8d201dd2a034047" dependencies = [ - "malloc_buf", + "block2", + "objc2", + "objc2-core-data", + "objc2-foundation", ] [[package]] -name = "objc-foundation" -version = "0.1.1" +name = "objc2-core-data" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +checksum = "6e092bc42eaf30a08844e6a076938c60751225ec81431ab89f5d1ccd9f958d6c" dependencies = [ - "block", - "objc", - "objc_id", + "block2", + "objc2", + "objc2-foundation", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-encode" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88658da63e4cc2c8adb1262902cd6af51094df0488b760d6fd27194269c0950a" + +[[package]] +name = "objc2-foundation" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "cfaefe14254871ea16c7d88968c0ff14ba554712a20d76421eec52f0a7fb8904" dependencies = [ - "objc", + "block2", + "dispatch", + "objc2", ] [[package]] @@ -1065,17 +1086,16 @@ name = "rfd" version = "0.14.1" dependencies = [ "ashpd", - "block", - "dispatch", + "block2", "futures", "glib-sys", "gobject-sys", "gtk-sys", "js-sys", "log", - "objc", - "objc-foundation", - "objc_id", + "objc2", + "objc2-app-kit", + "objc2-foundation", "pollster", "raw-window-handle", "urlencoding", diff --git a/Cargo.toml b/Cargo.toml index d2c2767..c9f646f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,11 +29,30 @@ raw-window-handle = "0.6" log = "0.4" [target.'cfg(target_os = "macos")'.dependencies] -objc = "0.2.7" -objc_id = "0.1.1" -dispatch = "0.2.0" -block = "0.1.6" -objc-foundation = "0.1.1" +block2 = "0.5.0" +objc2 = "0.5.1" +objc2-foundation = { version = "0.2.0", features = [ + "dispatch", + "NSArray", + "NSEnumerator", + "NSString", + "NSThread", + "NSURL", +] } +objc2-app-kit = { version = "0.2.0", features = [ + "block2", + "NSAlert", + "NSApplication", + "NSButton", + "NSControl", + "NSOpenPanel", + "NSPanel", + "NSResponder", + "NSRunningApplication", + "NSSavePanel", + "NSView", + "NSWindow", +] } [target.'cfg(target_os = "windows")'.dependencies] windows-sys = { version = "0.48", features = [ diff --git a/src/backend/macos.rs b/src/backend/macos.rs index 9486116..f0ffe1f 100644 --- a/src/backend/macos.rs +++ b/src/backend/macos.rs @@ -4,8 +4,3 @@ mod message_dialog; mod modal_future; mod utils; - -use objc::runtime::Object; -trait AsModal { - fn modal_ptr(&mut self) -> *mut Object; -} diff --git a/src/backend/macos/file_dialog.rs b/src/backend/macos/file_dialog.rs index 910bd7b..b1487fb 100644 --- a/src/backend/macos/file_dialog.rs +++ b/src/backend/macos/file_dialog.rs @@ -1,14 +1,15 @@ +use objc2::rc::autoreleasepool; +use objc2_app_kit::NSModalResponseOK; +use std::path::PathBuf; + mod panel_ffi; -use panel_ffi::Panel; +use self::panel_ffi::Panel; +use super::modal_future::ModalFuture; +use super::utils::{run_on_main, window_from_raw_window_handle}; use crate::backend::DialogFutureType; use crate::{FileDialog, FileHandle}; -use std::path::PathBuf; - -use super::modal_future::ModalFuture; -use super::utils::{run_on_main, INSWindow, NSWindow}; - // // File Picker // @@ -16,11 +17,11 @@ use super::utils::{run_on_main, INSWindow, NSWindow}; use crate::backend::FilePickerDialogImpl; impl FilePickerDialogImpl for FileDialog { fn pick_file(self) -> Option { - objc::rc::autoreleasepool(move || { - run_on_main(move || { - let panel = Panel::build_pick_file(&self); + autoreleasepool(move |_| { + run_on_main(move |mtm| { + let panel = Panel::build_pick_file(&self, mtm); - if panel.run_modal() == 1 { + if panel.run_modal() == NSModalResponseOK { Some(panel.get_result()) } else { None @@ -30,11 +31,11 @@ impl FilePickerDialogImpl for FileDialog { } fn pick_files(self) -> Option> { - objc::rc::autoreleasepool(move || { - run_on_main(move || { - let panel = Panel::build_pick_files(&self); + autoreleasepool(move |_| { + run_on_main(move |mtm| { + let panel = Panel::build_pick_files(&self, mtm); - if panel.run_modal() == 1 { + if panel.run_modal() == NSModalResponseOK { Some(panel.get_results()) } else { None @@ -47,13 +48,13 @@ impl FilePickerDialogImpl for FileDialog { use crate::backend::AsyncFilePickerDialogImpl; impl AsyncFilePickerDialogImpl for FileDialog { fn pick_file_async(self) -> DialogFutureType> { - let win = self.parent.as_ref().map(NSWindow::from_raw_window_handle); + let win = self.parent.as_ref().map(window_from_raw_window_handle); let future = ModalFuture::new( win, - move || Panel::build_pick_file(&self), + move |mtm| Panel::build_pick_file(&self, mtm), |panel, res_id| { - if res_id == 1 { + if res_id == NSModalResponseOK { Some(panel.get_result().into()) } else { None @@ -65,13 +66,13 @@ impl AsyncFilePickerDialogImpl for FileDialog { } fn pick_files_async(self) -> DialogFutureType>> { - let win = self.parent.as_ref().map(NSWindow::from_raw_window_handle); + let win = self.parent.as_ref().map(window_from_raw_window_handle); let future = ModalFuture::new( win, - move || Panel::build_pick_files(&self), + move |mtm| Panel::build_pick_files(&self, mtm), |panel, res_id| { - if res_id == 1 { + if res_id == NSModalResponseOK { Some( panel .get_results() @@ -96,10 +97,10 @@ impl AsyncFilePickerDialogImpl for FileDialog { use crate::backend::FolderPickerDialogImpl; impl FolderPickerDialogImpl for FileDialog { fn pick_folder(self) -> Option { - objc::rc::autoreleasepool(move || { - run_on_main(move || { - let panel = Panel::build_pick_folder(&self); - if panel.run_modal() == 1 { + autoreleasepool(move |_| { + run_on_main(move |mtm| { + let panel = Panel::build_pick_folder(&self, mtm); + if panel.run_modal() == NSModalResponseOK { Some(panel.get_result()) } else { None @@ -109,10 +110,10 @@ impl FolderPickerDialogImpl for FileDialog { } fn pick_folders(self) -> Option> { - objc::rc::autoreleasepool(move || { - run_on_main(move || { - let panel = Panel::build_pick_folders(&self); - if panel.run_modal() == 1 { + autoreleasepool(move |_| { + run_on_main(move |mtm| { + let panel = Panel::build_pick_folders(&self, mtm); + if panel.run_modal() == NSModalResponseOK { Some(panel.get_results()) } else { None @@ -125,13 +126,13 @@ impl FolderPickerDialogImpl for FileDialog { use crate::backend::AsyncFolderPickerDialogImpl; impl AsyncFolderPickerDialogImpl for FileDialog { fn pick_folder_async(self) -> DialogFutureType> { - let win = self.parent.as_ref().map(NSWindow::from_raw_window_handle); + let win = self.parent.as_ref().map(window_from_raw_window_handle); let future = ModalFuture::new( win, - move || Panel::build_pick_folder(&self), + move |mtm| Panel::build_pick_folder(&self, mtm), |panel, res_id| { - if res_id == 1 { + if res_id == NSModalResponseOK { Some(panel.get_result().into()) } else { None @@ -143,13 +144,13 @@ impl AsyncFolderPickerDialogImpl for FileDialog { } fn pick_folders_async(self) -> DialogFutureType>> { - let win = self.parent.as_ref().map(NSWindow::from_raw_window_handle); + let win = self.parent.as_ref().map(window_from_raw_window_handle); let future = ModalFuture::new( win, - move || Panel::build_pick_folders(&self), + move |mtm| Panel::build_pick_folders(&self, mtm), |panel, res_id| { - if res_id == 1 { + if res_id == NSModalResponseOK { Some( panel .get_results() @@ -174,10 +175,10 @@ impl AsyncFolderPickerDialogImpl for FileDialog { use crate::backend::FileSaveDialogImpl; impl FileSaveDialogImpl for FileDialog { fn save_file(self) -> Option { - objc::rc::autoreleasepool(move || { - run_on_main(move || { - let panel = Panel::build_save_file(&self); - if panel.run_modal() == 1 { + autoreleasepool(move |_| { + run_on_main(move |mtm| { + let panel = Panel::build_save_file(&self, mtm); + if panel.run_modal() == NSModalResponseOK { Some(panel.get_result()) } else { None @@ -190,13 +191,13 @@ impl FileSaveDialogImpl for FileDialog { use crate::backend::AsyncFileSaveDialogImpl; impl AsyncFileSaveDialogImpl for FileDialog { fn save_file_async(self) -> DialogFutureType> { - let win = self.parent.as_ref().map(NSWindow::from_raw_window_handle); + let win = self.parent.as_ref().map(window_from_raw_window_handle); let future = ModalFuture::new( win, - move || Panel::build_save_file(&self), + move |mtm| Panel::build_save_file(&self, mtm), |panel, res_id| { - if res_id == 1 { + if res_id == NSModalResponseOK { Some(panel.get_result().into()) } else { None diff --git a/src/backend/macos/file_dialog/panel_ffi.rs b/src/backend/macos/file_dialog/panel_ffi.rs index 7b3bbfe..24e9089 100644 --- a/src/backend/macos/file_dialog/panel_ffi.rs +++ b/src/backend/macos/file_dialog/panel_ffi.rs @@ -1,115 +1,133 @@ -use crate::FileDialog; - -use std::mem; +use core::str; use std::path::Path; -use std::{ops::DerefMut, path::PathBuf}; +use std::path::PathBuf; -use objc::{class, msg_send, sel, sel_impl}; -use objc_id::{Id, Shared}; +use block2::Block; +use objc2::rc::Id; -use super::super::utils::{INSURL, NSURL}; - -use crate::backend::macos::utils::{INSWindow, NSWindow}; -use objc::runtime::{Object, YES}; -use objc::runtime::{BOOL, NO}; -use objc_foundation::{INSArray, INSString, NSArray, NSString}; +use objc2_app_kit::{NSModalResponse, NSOpenPanel, NSSavePanel, NSWindow, NSWindowLevel}; +use objc2_foundation::{MainThreadMarker, NSArray, NSObjectProtocol, NSString, NSURL}; use raw_window_handle::RawWindowHandle; use super::super::{ + modal_future::{AsModal, InnerModal}, utils::{FocusManager, PolicyManager}, - AsModal, }; +use crate::backend::macos::utils::window_from_raw_window_handle; +use crate::FileDialog; extern "C" { pub fn CGShieldingWindowLevel() -> i32; } -fn make_nsstring(s: &str) -> Id { - NSString::from_str(s) -} - pub struct Panel { - pub(crate) panel: Id, - parent: Option>, + // Either `NSSavePanel` or the subclass `NSOpenPanel` + pub(crate) panel: Id, + parent: Option>, _focus_manager: FocusManager, _policy_manager: PolicyManager, } impl AsModal for Panel { - fn modal_ptr(&mut self) -> *mut Object { - self.panel.deref_mut() + fn inner_modal(&self) -> &NSSavePanel { + &*self.panel + } +} + +impl InnerModal for NSSavePanel { + fn begin_modal(&self, window: &NSWindow, handler: &Block) { + unsafe { self.beginSheetModalForWindow_completionHandler(window, handler) } + } + + fn run_modal(&self) -> NSModalResponse { + unsafe { self.runModal() } } } impl Panel { - pub fn new(panel: *mut Object) -> Self { - let _policy_manager = PolicyManager::new(); + fn as_open_panel(&self) -> Option<&NSOpenPanel> { + if self.panel.is_kind_of::() { + let ptr: *const NSSavePanel = &*self.panel; + let ptr: *const NSOpenPanel = ptr.cast(); + // SAFETY: Just checked that the panel is an `NSOpenPanel` + Some(unsafe { &*ptr }) + } else { + None + } + } - let _focus_manager = FocusManager::new(); + pub fn new(panel: Id, parent: Option<&RawWindowHandle>) -> Self { + let _policy_manager = PolicyManager::new(MainThreadMarker::from(&*panel)); - let _: () = unsafe { msg_send![panel, setLevel: CGShieldingWindowLevel()] }; + let _focus_manager = FocusManager::new(MainThreadMarker::from(&*panel)); + + panel.setLevel(unsafe { CGShieldingWindowLevel() } as NSWindowLevel); Self { - panel: unsafe { Id::from_ptr(panel) }, + panel, + parent: parent.map(window_from_raw_window_handle), _focus_manager, _policy_manager, - parent: None, } } - pub fn open_panel() -> Self { - Self::new(unsafe { msg_send![class!(NSOpenPanel), openPanel] }) - } - - pub fn save_panel() -> Self { - Self::new(unsafe { msg_send![class!(NSSavePanel), savePanel] }) - } - - pub fn run_modal(&self) -> i32 { + pub fn run_modal(&self) -> NSModalResponse { if let Some(parent) = self.parent.clone() { - let completion = { block::ConcreteBlock::new(|_: isize| {}) }; + let completion = block2::StackBlock::new(|_: isize| {}); unsafe { - msg_send![self.panel, beginSheetModalForWindow: parent completionHandler: &completion] + self.panel + .beginSheetModalForWindow_completionHandler(&parent, &completion) } - - mem::forget(completion); } - unsafe { msg_send![self.panel, runModal] } + unsafe { self.panel.runModal() } } - pub fn set_can_choose_directories(&self, v: BOOL) { - let _: () = unsafe { msg_send![self.panel, setCanChooseDirectories: v] }; + pub fn get_result(&self) -> PathBuf { + unsafe { + let url = self.panel.URL().unwrap(); + url.path().unwrap().to_string().into() + } } - pub fn set_can_create_directories(&self, v: BOOL) { - let _: () = unsafe { msg_send![self.panel, setCanCreateDirectories: v] }; - } + pub fn get_results(&self) -> Vec { + unsafe { + let urls = self.as_open_panel().unwrap().URLs(); + + let mut res = Vec::new(); + for url in urls { + res.push(url.path().unwrap().to_string().into()); + } - pub fn set_can_choose_files(&self, v: BOOL) { - let _: () = unsafe { msg_send![self.panel, setCanChooseFiles: v] }; + res + } } +} - pub fn set_allows_multiple_selection(&self, v: BOOL) { - let _: () = unsafe { msg_send![self.panel, setAllowsMultipleSelection: v] }; +trait PanelExt { + fn panel(&self) -> &NSSavePanel; + + fn set_can_create_directories(&self, can: bool) { + unsafe { self.panel().setCanCreateDirectories(can) } } - pub fn add_filters(&self, params: &FileDialog) { + fn add_filters(&self, opt: &FileDialog) { let mut exts: Vec = Vec::new(); - for filter in params.filters.iter() { + for filter in opt.filters.iter() { exts.append(&mut filter.extensions.to_vec()); } - unsafe { - let f_raw: Vec<_> = exts.iter().map(|ext| make_nsstring(&ext)).collect(); - let array = NSArray::from_vec(f_raw); + let f_raw: Vec<_> = exts.iter().map(|ext| NSString::from_str(&ext)).collect(); + let array = NSArray::from_vec(f_raw); - let _: () = msg_send![self.panel, setAllowedFileTypes: array]; + unsafe { + #[allow(deprecated)] + self.panel().setAllowedFileTypes(Some(&array)); } } - pub fn set_path(&self, path: &Path, file_name: Option<&str>) { + fn set_path(&self, path: &Path, file_name: Option<&str>) { // if file_name is some, and path is a dir let path = if let (Some(name), true) = (file_name, path.is_dir()) { let mut path = path.to_owned(); @@ -122,56 +140,39 @@ impl Panel { if let Some(path) = path.to_str() { unsafe { - let url = NSURL::file_url_with_path(path, true); - let () = msg_send![self.panel, setDirectoryURL: url]; + let url = NSURL::fileURLWithPath_isDirectory(&NSString::from_str(path), true); + self.panel().setDirectoryURL(Some(&url)); } } } - pub fn set_file_name(&self, file_name: &str) { - unsafe { - let file_name = make_nsstring(file_name); - let () = msg_send![self.panel, setNameFieldStringValue: file_name]; - } - } - - pub fn set_title(&self, title: &str) { + fn set_file_name(&self, file_name: &str) { unsafe { - let title = make_nsstring(title); - let () = msg_send![self.panel, setMessage: title]; + self.panel() + .setNameFieldStringValue(&NSString::from_str(file_name)) } } - pub fn set_parent(&mut self, parent: &RawWindowHandle) { - self.parent = Some(NSWindow::from_raw_window_handle(parent).share()); + fn set_title(&self, title: &str) { + unsafe { self.panel().setMessage(Some(&NSString::from_str(title))) } } +} - pub fn get_result(&self) -> PathBuf { - unsafe { - let url = msg_send![self.panel, URL]; - let url: Id = Id::from_ptr(url); - url.to_path_buf() - } +impl PanelExt for Id { + fn panel(&self) -> &NSSavePanel { + &self } +} - pub fn get_results(&self) -> Vec { - unsafe { - let urls = msg_send![self.panel, URLs]; - let urls: Id> = Id::from_ptr(urls); - - let mut res = Vec::new(); - for url in urls.to_vec() { - res.push(url.to_path_buf()); - } - - res - } +impl PanelExt for Id { + fn panel(&self) -> &NSSavePanel { + &self } } impl Panel { - pub fn build_pick_file(opt: &FileDialog) -> Self { - let mut panel = Panel::open_panel(); + pub fn build_pick_file(opt: &FileDialog, mtm: MainThreadMarker) -> Self { + let panel = unsafe { NSOpenPanel::openPanel(mtm) }; if !opt.filters.is_empty() { panel.add_filters(&opt); @@ -189,25 +190,18 @@ impl Panel { panel.set_title(title); } - if let Some(parent) = &opt.parent { - panel.set_parent(parent); - } - if let Some(can) = opt.can_create_directories { - panel.set_can_create_directories(match can { - true => YES, - false => NO, - }); + panel.set_can_create_directories(can); } - panel.set_can_choose_directories(NO); - panel.set_can_choose_files(YES); + unsafe { panel.setCanChooseDirectories(false) }; + unsafe { panel.setCanChooseFiles(true) }; - panel + Self::new(Id::into_super(panel), opt.parent.as_ref()) } - pub fn build_save_file(opt: &FileDialog) -> Self { - let mut panel = Panel::save_panel(); + pub fn build_save_file(opt: &FileDialog, mtm: MainThreadMarker) -> Self { + let panel = unsafe { NSSavePanel::savePanel(mtm) }; if !opt.filters.is_empty() { panel.add_filters(&opt); @@ -225,22 +219,15 @@ impl Panel { panel.set_title(title); } - if let Some(parent) = &opt.parent { - panel.set_parent(parent); - } - if let Some(can) = opt.can_create_directories { - panel.set_can_create_directories(match can { - true => YES, - false => NO, - }); + panel.set_can_create_directories(can); } - panel + Self::new(panel, opt.parent.as_ref()) } - pub fn build_pick_folder(opt: &FileDialog) -> Self { - let mut panel = Panel::open_panel(); + pub fn build_pick_folder(opt: &FileDialog, mtm: MainThreadMarker) -> Self { + let panel = unsafe { NSOpenPanel::openPanel(mtm) }; if let Some(path) = &opt.starting_directory { panel.set_path(path, opt.file_name.as_deref()); @@ -250,24 +237,17 @@ impl Panel { panel.set_title(title); } - if let Some(parent) = &opt.parent { - panel.set_parent(parent); - } - - let can_create_directories = opt.can_create_directories.unwrap_or(true); - panel.set_can_create_directories(match can_create_directories { - true => YES, - false => NO, - }); + let can = opt.can_create_directories.unwrap_or(true); + panel.set_can_create_directories(can); - panel.set_can_choose_directories(YES); - panel.set_can_choose_files(NO); + unsafe { panel.setCanChooseDirectories(true) }; + unsafe { panel.setCanChooseFiles(false) }; - panel + Self::new(Id::into_super(panel), opt.parent.as_ref()) } - pub fn build_pick_folders(opt: &FileDialog) -> Self { - let mut panel = Panel::open_panel(); + pub fn build_pick_folders(opt: &FileDialog, mtm: MainThreadMarker) -> Self { + let panel = unsafe { NSOpenPanel::openPanel(mtm) }; if let Some(path) = &opt.starting_directory { panel.set_path(path, opt.file_name.as_deref()); @@ -277,25 +257,18 @@ impl Panel { panel.set_title(title); } - if let Some(parent) = &opt.parent { - panel.set_parent(parent); - } - let can = opt.can_create_directories.unwrap_or(true); - panel.set_can_create_directories(match can { - true => YES, - false => NO, - }); + panel.set_can_create_directories(can); - panel.set_can_choose_directories(YES); - panel.set_can_choose_files(NO); - panel.set_allows_multiple_selection(YES); + unsafe { panel.setCanChooseDirectories(true) }; + unsafe { panel.setCanChooseFiles(false) }; + unsafe { panel.setAllowsMultipleSelection(true) }; - panel + Self::new(Id::into_super(panel), opt.parent.as_ref()) } - pub fn build_pick_files(opt: &FileDialog) -> Self { - let mut panel = Panel::open_panel(); + pub fn build_pick_files(opt: &FileDialog, mtm: MainThreadMarker) -> Self { + let panel = unsafe { NSOpenPanel::openPanel(mtm) }; if !opt.filters.is_empty() { panel.add_filters(&opt); @@ -309,21 +282,14 @@ impl Panel { panel.set_title(title); } - if let Some(parent) = &opt.parent { - panel.set_parent(parent); - } - if let Some(can) = opt.can_create_directories { - panel.set_can_create_directories(match can { - true => YES, - false => NO, - }); + panel.set_can_create_directories(can); } - panel.set_can_choose_directories(NO); - panel.set_can_choose_files(YES); - panel.set_allows_multiple_selection(YES); + unsafe { panel.setCanChooseDirectories(false) }; + unsafe { panel.setCanChooseFiles(true) }; + unsafe { panel.setAllowsMultipleSelection(true) }; - panel + Self::new(Id::into_super(panel), opt.parent.as_ref()) } } diff --git a/src/backend/macos/message_dialog.rs b/src/backend/macos/message_dialog.rs index b75dd63..14d3409 100644 --- a/src/backend/macos/message_dialog.rs +++ b/src/backend/macos/message_dialog.rs @@ -1,51 +1,35 @@ -use std::mem; -use std::ops::DerefMut; - use crate::backend::DialogFutureType; use crate::message_dialog::{MessageButtons, MessageDialog, MessageDialogResult, MessageLevel}; +use super::modal_future::AsModal; use super::{ - modal_future::ModalFuture, + modal_future::{InnerModal, ModalFuture}, utils::{run_on_main, FocusManager, PolicyManager}, - AsModal, }; -use super::utils::{INSApplication, INSWindow, NSApplication, NSWindow}; -use objc::runtime::Object; -use objc::{class, msg_send, sel, sel_impl}; -use objc_foundation::{INSString, NSString}; - -use objc_id::Id; - -#[repr(i64)] -#[derive(Debug, PartialEq)] -enum NSAlertStyle { - Warning = 0, - Informational = 1, - Critical = 2, -} +use super::utils::window_from_raw_window_handle; +use block2::Block; +use objc2_app_kit::{ + NSAlert, NSAlertFirstButtonReturn, NSAlertSecondButtonReturn, NSAlertStyle, + NSAlertThirdButtonReturn, NSApplication, NSModalResponse, NSWindow, +}; +use objc2_foundation::{MainThreadMarker, NSString}; -#[repr(i64)] -#[derive(Debug, PartialEq)] -enum NSAlertReturn { - FirstButton = 1000, - SecondButton = 1001, - ThirdButton = 1002, -} +use objc2::rc::{autoreleasepool, Id}; -pub struct NSAlert { +pub struct Alert { buttons: MessageButtons, - alert: Id, + alert: Id, parent: Option>, _focus_manager: FocusManager, _policy_manager: PolicyManager, } -impl NSAlert { - pub fn new(opt: MessageDialog) -> Self { - let _policy_manager = PolicyManager::new(); +impl Alert { + pub fn new(opt: MessageDialog, mtm: MainThreadMarker) -> Self { + let _policy_manager = PolicyManager::new(mtm); - let alert: *mut Object = unsafe { msg_send![class!(NSAlert), new] }; + let alert = unsafe { NSAlert::new(mtm) }; let level = match opt.level { MessageLevel::Info => NSAlertStyle::Informational, @@ -53,9 +37,7 @@ impl NSAlert { MessageLevel::Error => NSAlertStyle::Critical, }; - unsafe { - let _: () = msg_send![alert, setAlertStyle: level as i64]; - } + unsafe { alert.setAlertStyle(level) }; let buttons = match &opt.buttons { MessageButtons::Ok => vec!["OK".to_owned()], @@ -78,24 +60,22 @@ impl NSAlert { }; for button in buttons { - unsafe { - let label = NSString::from_str(&button); - let _: () = msg_send![alert, addButtonWithTitle: label]; - } + let label = NSString::from_str(&button); + unsafe { alert.addButtonWithTitle(&label) }; } unsafe { let text = NSString::from_str(&opt.title); - let _: () = msg_send![alert, setMessageText: text]; + alert.setMessageText(&text); let text = NSString::from_str(&opt.description); - let _: () = msg_send![alert, setInformativeText: text]; + alert.setInformativeText(&text); } - let _focus_manager = FocusManager::new(); + let _focus_manager = FocusManager::new(mtm); Self { - alert: unsafe { Id::from_retained_ptr(alert) }, - parent: opt.parent.map(|x| NSWindow::from_raw_window_handle(&x)), + alert, + parent: opt.parent.map(|x| window_from_raw_window_handle(&x)), buttons: opt.buttons, _focus_manager, _policy_manager, @@ -103,89 +83,79 @@ impl NSAlert { } pub fn run(mut self) -> MessageDialogResult { + let mtm = MainThreadMarker::from(&*self.alert); + if let Some(parent) = self.parent.take() { let completion = { - block::ConcreteBlock::new(|result: isize| { - let _: () = unsafe { - msg_send![NSApplication::shared_application(), stopModalWithCode: result] - }; + block2::StackBlock::new(move |result| unsafe { + NSApplication::sharedApplication(mtm).stopModalWithCode(result); }) }; unsafe { - msg_send![self.alert, beginSheetModalForWindow: parent completionHandler: &completion] + self.alert + .beginSheetModalForWindow_completionHandler(&parent, Some(&completion)) } - - mem::forget(completion); } - let ret: i64 = unsafe { msg_send![self.alert, runModal] }; - dialog_result(&self.buttons, ret) + dialog_result(&self.buttons, unsafe { self.alert.runModal() }) } } -fn dialog_result(buttons: &MessageButtons, ret: i64) -> MessageDialogResult { +fn dialog_result(buttons: &MessageButtons, ret: NSModalResponse) -> MessageDialogResult { match buttons { - MessageButtons::Ok if ret == NSAlertReturn::FirstButton as i64 => MessageDialogResult::Ok, - MessageButtons::OkCancel if ret == NSAlertReturn::FirstButton as i64 => { - MessageDialogResult::Ok - } - MessageButtons::OkCancel if ret == NSAlertReturn::SecondButton as i64 => { - MessageDialogResult::Cancel - } - MessageButtons::YesNo if ret == NSAlertReturn::FirstButton as i64 => { - MessageDialogResult::Yes - } - MessageButtons::YesNo if ret == NSAlertReturn::SecondButton as i64 => { - MessageDialogResult::No - } - MessageButtons::YesNoCancel if ret == NSAlertReturn::FirstButton as i64 => { - MessageDialogResult::Yes - } - MessageButtons::YesNoCancel if ret == NSAlertReturn::SecondButton as i64 => { - MessageDialogResult::No - } - MessageButtons::YesNoCancel if ret == NSAlertReturn::ThirdButton as i64 => { + MessageButtons::Ok if ret == NSAlertFirstButtonReturn => MessageDialogResult::Ok, + MessageButtons::OkCancel if ret == NSAlertFirstButtonReturn => MessageDialogResult::Ok, + MessageButtons::OkCancel if ret == NSAlertSecondButtonReturn => MessageDialogResult::Cancel, + MessageButtons::YesNo if ret == NSAlertFirstButtonReturn => MessageDialogResult::Yes, + MessageButtons::YesNo if ret == NSAlertSecondButtonReturn => MessageDialogResult::No, + MessageButtons::YesNoCancel if ret == NSAlertFirstButtonReturn => MessageDialogResult::Yes, + MessageButtons::YesNoCancel if ret == NSAlertSecondButtonReturn => MessageDialogResult::No, + MessageButtons::YesNoCancel if ret == NSAlertThirdButtonReturn => { MessageDialogResult::Cancel } - MessageButtons::OkCustom(custom) if ret == NSAlertReturn::FirstButton as i64 => { + MessageButtons::OkCustom(custom) if ret == NSAlertFirstButtonReturn => { MessageDialogResult::Custom(custom.to_owned()) } - MessageButtons::OkCancelCustom(custom, _) if ret == NSAlertReturn::FirstButton as i64 => { + MessageButtons::OkCancelCustom(custom, _) if ret == NSAlertFirstButtonReturn => { MessageDialogResult::Custom(custom.to_owned()) } - MessageButtons::OkCancelCustom(_, custom) if ret == NSAlertReturn::SecondButton as i64 => { + MessageButtons::OkCancelCustom(_, custom) if ret == NSAlertSecondButtonReturn => { MessageDialogResult::Custom(custom.to_owned()) } - MessageButtons::YesNoCancelCustom(custom, _, _) - if ret == NSAlertReturn::FirstButton as i64 => - { + MessageButtons::YesNoCancelCustom(custom, _, _) if ret == NSAlertFirstButtonReturn => { MessageDialogResult::Custom(custom.to_owned()) } - MessageButtons::YesNoCancelCustom(_, custom, _) - if ret == NSAlertReturn::SecondButton as i64 => - { + MessageButtons::YesNoCancelCustom(_, custom, _) if ret == NSAlertSecondButtonReturn => { MessageDialogResult::Custom(custom.to_owned()) } - MessageButtons::YesNoCancelCustom(_, _, custom) - if ret == NSAlertReturn::ThirdButton as i64 => - { + MessageButtons::YesNoCancelCustom(_, _, custom) if ret == NSAlertThirdButtonReturn => { MessageDialogResult::Custom(custom.to_owned()) } _ => MessageDialogResult::Cancel, } } -impl AsModal for NSAlert { - fn modal_ptr(&mut self) -> *mut Object { - self.alert.deref_mut() +impl AsModal for Alert { + fn inner_modal(&self) -> &NSAlert { + &*self.alert + } +} + +impl InnerModal for NSAlert { + fn begin_modal(&self, window: &NSWindow, handler: &Block) { + unsafe { self.beginSheetModalForWindow_completionHandler(window, Some(handler)) } + } + + fn run_modal(&self) -> NSModalResponse { + unsafe { self.runModal() } } } use crate::backend::MessageDialogImpl; impl MessageDialogImpl for MessageDialog { fn show(self) -> MessageDialogResult { - objc::rc::autoreleasepool(move || run_on_main(move || NSAlert::new(self).run())) + autoreleasepool(move |_| run_on_main(move |mtm| Alert::new(self, mtm).run())) } } @@ -193,11 +163,11 @@ use crate::backend::AsyncMessageDialogImpl; impl AsyncMessageDialogImpl for MessageDialog { fn show_async(self) -> DialogFutureType { - let win = self.parent.as_ref().map(NSWindow::from_raw_window_handle); + let win = self.parent.as_ref().map(window_from_raw_window_handle); let future = ModalFuture::new( win, - move || NSAlert::new(self), + move |mtm| Alert::new(self, mtm), |dialog, ret| dialog_result(&dialog.buttons, ret), ); Box::pin(future) diff --git a/src/backend/macos/modal_future.rs b/src/backend/macos/modal_future.rs index 8534748..b264d7f 100644 --- a/src/backend/macos/modal_future.rs +++ b/src/backend/macos/modal_future.rs @@ -1,16 +1,25 @@ -use objc::{msg_send, sel, sel_impl}; -use objc_id::Id; - +use block2::Block; +use objc2::mutability::MainThreadOnly; +use objc2::rc::Id; +use objc2::ClassType; +use objc2_app_kit::{NSApplication, NSModalResponse, NSWindow}; +use objc2_foundation::{run_on_main, MainThreadMarker}; + +use std::pin::Pin; use std::sync::{Arc, Mutex}; -use std::{mem, pin::Pin}; use std::task::{Context, Poll, Waker}; -use super::AsModal; +use super::utils::activate_cocoa_multithreading; + +pub(super) trait AsModal { + fn inner_modal(&self) -> &(impl InnerModal + 'static); +} -use super::utils::{ - activate_cocoa_multithreading, is_main_thread, INSApplication, NSApplication, NSWindow, -}; +pub(super) trait InnerModal: ClassType { + fn begin_modal(&self, window: &NSWindow, handler: &Block); + fn run_modal(&self) -> NSModalResponse; +} struct FutureState { waker: Option, @@ -27,13 +36,13 @@ pub(super) struct ModalFuture { unsafe impl Send for ModalFuture {} impl ModalFuture { - pub fn new D + Send>( + pub fn new D + Send>( win: Option>, build_modal: DBULD, cb: F, ) -> Self where - F: Fn(&mut D, i64) -> R + Send + 'static, + F: Fn(&mut D, isize) -> R + Send + 'static, { activate_cocoa_multithreading(); @@ -43,7 +52,8 @@ impl ModalFuture { modal: None, })); - let dialog_callback = move |state: Arc>>, result: i64| { + let dialog_callback = move |state: Arc>>, + result: NSModalResponse| { let mut state = state.lock().unwrap(); // take() to drop it when it's safe to do so state.data = if let Some(mut modal) = state.modal.take() { @@ -56,59 +66,52 @@ impl ModalFuture { } }; - let app = NSApplication::shared_application(); + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + let app = NSApplication::sharedApplication(mtm); let win = if let Some(win) = win { - Some(win.share()) + Some(win) } else { - app.get_window() + unsafe { app.mainWindow() }.or_else(|| app.windows().first_retained()) }; // if async exec is possible start sheet modal // otherwise fallback to sync - if app.is_running() && win.is_some() { + if unsafe { app.isRunning() } && win.is_some() { let state = state.clone(); - let main_runner = move || { + + // Hack to work around us getting the window above + struct WindowWrapper(Id); + unsafe impl Send for WindowWrapper {} + let window = WindowWrapper(win.unwrap()); + + run_on_main(move |mtm| { + let window = window; + let completion = { let state = state.clone(); - block::ConcreteBlock::new(move |result: i64| { + block2::RcBlock::new(move |result| { dialog_callback(state.clone(), result); }) }; - let window = win.unwrap(); - - let mut modal = build_modal(); - let modal_ptr = modal.modal_ptr(); + let modal = build_modal(mtm); + let inner = modal.inner_modal().retain(); state.lock().unwrap().modal = Some(modal); - let _: () = unsafe { - msg_send![ - modal_ptr, - beginSheetModalForWindow: window completionHandler: &completion - ] - }; - - mem::forget(completion); - }; - - if !is_main_thread() { - let main = dispatch::Queue::main(); - main.exec_sync(main_runner); - } else { - main_runner(); - } + inner.begin_modal(&window.0, &completion); + }); } else { eprintln!("\n Hi! It looks like you are running async dialog in unsupported environment, I will fallback to sync dialog for you. \n"); - if is_main_thread() { - let mut modal = build_modal(); - let modal_ptr = modal.modal_ptr(); + if let Some(mtm) = MainThreadMarker::new() { + let modal = build_modal(mtm); + let inner = modal.inner_modal().retain(); state.lock().unwrap().modal = Some(modal); - let ret: i64 = unsafe { msg_send![modal_ptr, runModal] }; + let ret = inner.run_modal(); dialog_callback(state.clone(), ret); } else { diff --git a/src/backend/macos/utils.rs b/src/backend/macos/utils.rs index 3d6104b..9fa3cd3 100644 --- a/src/backend/macos/utils.rs +++ b/src/backend/macos/utils.rs @@ -1,42 +1,42 @@ -use objc::runtime::Object; -use objc::{class, msg_send, sel, sel_impl}; - -mod application; mod focus_manager; mod policy_manager; -mod url; -mod window; - -pub use application::{INSApplication, NSApplication}; -pub use focus_manager::FocusManager; -pub use policy_manager::PolicyManager; -pub use url::{INSURL, NSURL}; -pub use window::{INSWindow, NSWindow}; -#[allow(non_upper_case_globals)] -pub const nil: *mut Object = 0 as *mut _; +pub use self::focus_manager::FocusManager; +pub use self::policy_manager::PolicyManager; -pub fn is_main_thread() -> bool { - unsafe { msg_send![class!(NSThread), isMainThread] } -} +use objc2::rc::Id; +use objc2_app_kit::{NSApplication, NSView, NSWindow}; +use objc2_foundation::{MainThreadMarker, NSThread}; +use raw_window_handle::RawWindowHandle; pub fn activate_cocoa_multithreading() { - unsafe { - let thread: *mut Object = msg_send![class!(NSThread), new]; - let _: () = msg_send![thread, start]; - } + let thread = NSThread::new(); + unsafe { thread.start() }; } -pub fn run_on_main R + Send>(run: F) -> R { - if is_main_thread() { - run() +pub fn run_on_main R + Send>(run: F) -> R { + if let Some(mtm) = MainThreadMarker::new() { + run(mtm) } else { - let app = NSApplication::shared_application(); - if app.is_running() { - let main = dispatch::Queue::main(); - main.exec_sync(run) + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + let app = NSApplication::sharedApplication(mtm); + if unsafe { app.isRunning() } { + objc2_foundation::run_on_main(|mtm| run(mtm)) } else { panic!("You are running RFD in NonWindowed environment, it is impossible to spawn dialog from thread different than main in this env."); } } } + +pub fn window_from_raw_window_handle(h: &RawWindowHandle) -> Id { + // TODO: Move this requirement up + let _mtm = unsafe { MainThreadMarker::new_unchecked() }; + match h { + RawWindowHandle::AppKit(h) => { + let view = h.ns_view.as_ptr() as *mut NSView; + let view = unsafe { Id::retain(view).unwrap() }; + view.window().expect("NSView to be inside a NSWindow") + } + _ => unreachable!("unsupported window handle, expected: MacOS"), + } +} diff --git a/src/backend/macos/utils/application.rs b/src/backend/macos/utils/application.rs deleted file mode 100644 index d830531..0000000 --- a/src/backend/macos/utils/application.rs +++ /dev/null @@ -1,58 +0,0 @@ -use objc::{class, msg_send, sel, sel_impl}; - -use objc_foundation::{object_struct, INSArray, INSObject, NSArray}; -use objc_id::{Id, ShareId, Shared}; - -use super::NSWindow; - -pub trait INSApplication: INSObject { - fn shared_application() -> Id { - let app = unsafe { msg_send![class!(NSApplication), sharedApplication] }; - unsafe { Id::from_ptr(app) } - } - - fn is_running(&self) -> bool { - unsafe { msg_send![self, isRunning] } - } - - fn key_window(&self) -> Option> { - let id: *mut NSWindow = unsafe { msg_send![self, keyWindow] }; - if id.is_null() { - None - } else { - Some(unsafe { Id::from_ptr(id) }) - } - } - - fn main_window(&self) -> Option> { - let id: *mut NSWindow = unsafe { msg_send![self, mainWindow] }; - if id.is_null() { - None - } else { - Some(unsafe { Id::from_ptr(id) }) - } - } - - fn windows(&self) -> Id> { - let id = unsafe { msg_send![self, windows] }; - let id: Id> = unsafe { Id::from_ptr(id) }; - id - } - - fn get_window(&self) -> Option> { - if let Some(key_window) = self.main_window() { - Some(key_window.share()) - } else { - let windows = self.windows(); - if windows.count() > 0 { - let window = windows.shared_object_at(0); - Some(window) - } else { - None - } - } - } -} - -object_struct!(NSApplication); -impl INSApplication for NSApplication {} diff --git a/src/backend/macos/utils/focus_manager.rs b/src/backend/macos/utils/focus_manager.rs index c8af2a3..4f75f41 100644 --- a/src/backend/macos/utils/focus_manager.rs +++ b/src/backend/macos/utils/focus_manager.rs @@ -1,15 +1,16 @@ -use objc_id::Id; +use objc2::rc::Id; -use super::{INSApplication, INSWindow, NSApplication, NSWindow}; +use objc2_app_kit::{NSApplication, NSWindow}; +use objc2_foundation::MainThreadMarker; pub struct FocusManager { key_window: Option>, } impl FocusManager { - pub fn new() -> Self { - let app = NSApplication::shared_application(); - let key_window = app.key_window(); + pub fn new(mtm: MainThreadMarker) -> Self { + let app = NSApplication::sharedApplication(mtm); + let key_window = app.keyWindow(); Self { key_window } } @@ -18,7 +19,7 @@ impl FocusManager { impl Drop for FocusManager { fn drop(&mut self) { if let Some(win) = &self.key_window { - win.make_key_and_order_front(); + win.makeKeyAndOrderFront(None); } } } diff --git a/src/backend/macos/utils/policy_manager.rs b/src/backend/macos/utils/policy_manager.rs index 853867c..4410701 100644 --- a/src/backend/macos/utils/policy_manager.rs +++ b/src/backend/macos/utils/policy_manager.rs @@ -1,41 +1,32 @@ -use objc::runtime::Object; -use objc::{class, msg_send, sel, sel_impl}; - -#[repr(i32)] -#[derive(Debug, PartialEq)] -enum ApplicationActivationPolicy { - //Regular = 0, - Accessory = 1, - Prohibited = 2, - //Error = -1, -} +use objc2::rc::Id; +use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy}; +use objc2_foundation::MainThreadMarker; pub struct PolicyManager { - initial_policy: i32, + app: Id, + initial_policy: NSApplicationActivationPolicy, } impl PolicyManager { - pub fn new() -> Self { - unsafe { - let app: *mut Object = msg_send![class!(NSApplication), sharedApplication]; - let initial_policy: i32 = msg_send![app, activationPolicy]; + pub fn new(mtm: MainThreadMarker) -> Self { + let app = NSApplication::sharedApplication(mtm); + let initial_policy = unsafe { app.activationPolicy() }; - if initial_policy == ApplicationActivationPolicy::Prohibited as i32 { - let new_pol = ApplicationActivationPolicy::Accessory as i32; - let _: () = msg_send![app, setActivationPolicy: new_pol]; - } + if initial_policy == NSApplicationActivationPolicy::Prohibited { + let new_pol = NSApplicationActivationPolicy::Accessory; + app.setActivationPolicy(new_pol); + } - Self { initial_policy } + Self { + app, + initial_policy, } } } impl Drop for PolicyManager { fn drop(&mut self) { - unsafe { - let app: *mut Object = msg_send![class!(NSApplication), sharedApplication]; - // Restore initial pol - let _: () = msg_send![app, setActivationPolicy: self.initial_policy]; - } + // Restore initial policy + self.app.setActivationPolicy(self.initial_policy); } } diff --git a/src/backend/macos/utils/url.rs b/src/backend/macos/utils/url.rs deleted file mode 100644 index 7789ac1..0000000 --- a/src/backend/macos/utils/url.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::path::PathBuf; - -use objc::{class, msg_send, sel, sel_impl}; - -use objc_foundation::{object_struct, INSObject, INSString, NSString}; -use objc_id::Id; - -pub trait INSURL: INSObject { - fn file_url_with_path(s: &str, is_dir: bool) -> Id { - let s = NSString::from_str(s); - let ptr = unsafe { msg_send![class!(NSURL), fileURLWithPath: s isDirectory:is_dir] }; - unsafe { Id::from_retained_ptr(ptr) } - } - - fn to_path_buf(&self) -> PathBuf { - let s = unsafe { msg_send![self, path] }; - let s: Id = unsafe { Id::from_ptr(s) }; - s.as_str().into() - } -} - -object_struct!(NSURL); -impl INSURL for NSURL {} diff --git a/src/backend/macos/utils/window.rs b/src/backend/macos/utils/window.rs deleted file mode 100644 index 3131424..0000000 --- a/src/backend/macos/utils/window.rs +++ /dev/null @@ -1,30 +0,0 @@ -use objc::{msg_send, sel, sel_impl}; -use objc_id::Id; - -use super::nil; -use objc_foundation::{object_struct, INSObject, NSObject}; - -use raw_window_handle::RawWindowHandle; - -pub trait INSWindow: INSObject { - fn from_raw_window_handle(h: &RawWindowHandle) -> Id { - match h { - RawWindowHandle::AppKit(h) => { - let id = h.ns_view.as_ptr() as *mut NSObject; - let id: Id = unsafe { Id::from_ptr(id) }; - - let window: *mut NSWindow = unsafe { msg_send![id, window] }; - - unsafe { Id::from_ptr(window as *mut Self) } - } - _ => unreachable!("unsupported window handle, expected: MacOS"), - } - } - - fn make_key_and_order_front(&self) { - let _: () = unsafe { msg_send![self, makeKeyAndOrderFront: nil] }; - } -} - -object_struct!(NSWindow); -impl INSWindow for NSWindow {} From f057981511757ebf58bb5283dde8514b2d3ab664 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 25 Apr 2024 22:03:41 +0200 Subject: [PATCH 2/2] Fix GitHub actions runner macos-latest is now macos-14, which runs on aarch64 --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 123d469..6169d52 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: flags: '--features common-controls-v6' - name: macOS os: macos-latest - target: x86_64-apple-darwin + target: aarch64-apple-darwin flags: '' - name: WASM32 os: ubuntu-latest