Skip to content

Commit

Permalink
macOS: Use objc2 crates (#188)
Browse files Browse the repository at this point in the history
* 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.

* Fix GitHub actions runner

macos-latest is now macos-14, which runs on aarch64
  • Loading branch information
madsmtm authored May 5, 2024
1 parent 7db7985 commit c1b2c85
Show file tree
Hide file tree
Showing 15 changed files with 403 additions and 545 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
88 changes: 54 additions & 34 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 24 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
5 changes: 0 additions & 5 deletions src/backend/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
83 changes: 42 additions & 41 deletions src/backend/macos/file_dialog.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
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
//

use crate::backend::FilePickerDialogImpl;
impl FilePickerDialogImpl for FileDialog {
fn pick_file(self) -> Option<PathBuf> {
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
Expand All @@ -30,11 +31,11 @@ impl FilePickerDialogImpl for FileDialog {
}

fn pick_files(self) -> Option<Vec<PathBuf>> {
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
Expand All @@ -47,13 +48,13 @@ impl FilePickerDialogImpl for FileDialog {
use crate::backend::AsyncFilePickerDialogImpl;
impl AsyncFilePickerDialogImpl for FileDialog {
fn pick_file_async(self) -> DialogFutureType<Option<FileHandle>> {
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
Expand All @@ -65,13 +66,13 @@ impl AsyncFilePickerDialogImpl for FileDialog {
}

fn pick_files_async(self) -> DialogFutureType<Option<Vec<FileHandle>>> {
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()
Expand All @@ -96,10 +97,10 @@ impl AsyncFilePickerDialogImpl for FileDialog {
use crate::backend::FolderPickerDialogImpl;
impl FolderPickerDialogImpl for FileDialog {
fn pick_folder(self) -> Option<PathBuf> {
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
Expand All @@ -109,10 +110,10 @@ impl FolderPickerDialogImpl for FileDialog {
}

fn pick_folders(self) -> Option<Vec<PathBuf>> {
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
Expand All @@ -125,13 +126,13 @@ impl FolderPickerDialogImpl for FileDialog {
use crate::backend::AsyncFolderPickerDialogImpl;
impl AsyncFolderPickerDialogImpl for FileDialog {
fn pick_folder_async(self) -> DialogFutureType<Option<FileHandle>> {
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
Expand All @@ -143,13 +144,13 @@ impl AsyncFolderPickerDialogImpl for FileDialog {
}

fn pick_folders_async(self) -> DialogFutureType<Option<Vec<FileHandle>>> {
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()
Expand All @@ -174,10 +175,10 @@ impl AsyncFolderPickerDialogImpl for FileDialog {
use crate::backend::FileSaveDialogImpl;
impl FileSaveDialogImpl for FileDialog {
fn save_file(self) -> Option<PathBuf> {
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
Expand All @@ -190,13 +191,13 @@ impl FileSaveDialogImpl for FileDialog {
use crate::backend::AsyncFileSaveDialogImpl;
impl AsyncFileSaveDialogImpl for FileDialog {
fn save_file_async(self) -> DialogFutureType<Option<FileHandle>> {
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
Expand Down
Loading

0 comments on commit c1b2c85

Please sign in to comment.