Prompt for paths asynchronously to avoid double borrow

This commit is contained in:
Antonio Scandurra 2021-04-14 16:30:03 +02:00
parent 29d2236ed2
commit cf23b0e4a2
7 changed files with 70 additions and 35 deletions

1
Cargo.lock generated
View file

@ -903,6 +903,7 @@ dependencies = [
"async-std", "async-std",
"async-task", "async-task",
"bindgen", "bindgen",
"block",
"cc", "cc",
"cocoa", "cocoa",
"core-foundation", "core-foundation",

View file

@ -37,6 +37,7 @@ simplelog = "0.9"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
anyhow = "1" anyhow = "1"
block = "0.1"
cocoa = "0.24" cocoa = "0.24"
core-foundation = "0.9" core-foundation = "0.9"
core-graphics = "0.22.2" core-graphics = "0.22.2"

View file

@ -5,7 +5,7 @@ use crate::{
platform::{self, WindowOptions}, platform::{self, WindowOptions},
presenter::Presenter, presenter::Presenter,
util::post_inc, util::post_inc,
AssetCache, AssetSource, ClipboardItem, FontCache, TextLayoutCache, AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use async_std::sync::Condvar; use async_std::sync::Condvar;
@ -570,6 +570,22 @@ impl MutableAppContext {
self.platform.set_menus(menus); self.platform.set_menus(menus);
} }
pub fn prompt_for_paths<F>(&self, options: PathPromptOptions, done_fn: F)
where
F: 'static + FnOnce(Option<Vec<PathBuf>>, &mut MutableAppContext),
{
let app = self.weak_self.as_ref().unwrap().upgrade().unwrap();
let foreground = self.foreground.clone();
self.platform().prompt_for_paths(
options,
Box::new(move |paths| {
foreground
.spawn(async move { (done_fn)(paths, &mut *app.borrow_mut()) })
.detach();
}),
);
}
pub fn dispatch_action<T: 'static + Any>( pub fn dispatch_action<T: 'static + Any>(
&mut self, &mut self,
window_id: usize, window_id: usize,

View file

@ -1,5 +1,6 @@
use super::{BoolExt as _, Dispatcher, FontSystem, Window}; use super::{BoolExt as _, Dispatcher, FontSystem, Window};
use crate::{executor, keymap::Keystroke, platform, ClipboardItem, Event, Menu, MenuItem}; use crate::{executor, keymap::Keystroke, platform, ClipboardItem, Event, Menu, MenuItem};
use block::ConcreteBlock;
use cocoa::{ use cocoa::{
appkit::{ appkit::{
NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
@ -20,7 +21,7 @@ use objc::{
use ptr::null_mut; use ptr::null_mut;
use std::{ use std::{
any::Any, any::Any,
cell::RefCell, cell::{Cell, RefCell},
convert::TryInto, convert::TryInto,
ffi::{c_void, CStr}, ffi::{c_void, CStr},
os::raw::c_char, os::raw::c_char,
@ -267,31 +268,40 @@ impl platform::Platform for MacPlatform {
fn prompt_for_paths( fn prompt_for_paths(
&self, &self,
options: platform::PathPromptOptions, options: platform::PathPromptOptions,
) -> Option<Vec<std::path::PathBuf>> { done_fn: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
) {
unsafe { unsafe {
let panel = NSOpenPanel::openPanel(nil); let panel = NSOpenPanel::openPanel(nil);
panel.setCanChooseDirectories_(options.directories.to_objc()); panel.setCanChooseDirectories_(options.directories.to_objc());
panel.setCanChooseFiles_(options.files.to_objc()); panel.setCanChooseFiles_(options.files.to_objc());
panel.setAllowsMultipleSelection_(options.multiple.to_objc()); panel.setAllowsMultipleSelection_(options.multiple.to_objc());
panel.setResolvesAliases_(false.to_objc()); panel.setResolvesAliases_(false.to_objc());
let response = panel.runModal(); let done_fn = Cell::new(Some(done_fn));
if response == NSModalResponse::NSModalResponseOk { let block = ConcreteBlock::new(move |response: NSModalResponse| {
let mut result = Vec::new(); let result = if response == NSModalResponse::NSModalResponseOk {
let urls = panel.URLs(); let mut result = Vec::new();
for i in 0..urls.count() { let urls = panel.URLs();
let url = urls.objectAtIndex(i); for i in 0..urls.count() {
let string = url.absoluteString(); let url = urls.objectAtIndex(i);
let string = std::ffi::CStr::from_ptr(string.UTF8String()) let string = url.absoluteString();
.to_string_lossy() let string = std::ffi::CStr::from_ptr(string.UTF8String())
.to_string(); .to_string_lossy()
if let Some(path) = string.strip_prefix("file://") { .to_string();
result.push(PathBuf::from(path)); if let Some(path) = string.strip_prefix("file://") {
result.push(PathBuf::from(path));
}
} }
Some(result)
} else {
None
};
if let Some(done_fn) = done_fn.take() {
(done_fn)(result);
} }
Some(result) });
} else { let block = block.copy();
None let _: () = msg_send![panel, beginWithCompletionHandler: block];
}
} }
} }

View file

@ -40,7 +40,11 @@ pub trait Platform {
executor: Rc<executor::Foreground>, executor: Rc<executor::Foreground>,
) -> Box<dyn Window>; ) -> Box<dyn Window>;
fn key_window_id(&self) -> Option<usize>; fn key_window_id(&self) -> Option<usize>;
fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>; fn prompt_for_paths(
&self,
options: PathPromptOptions,
done_fn: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
);
fn quit(&self); fn quit(&self);
fn write_to_clipboard(&self, item: ClipboardItem); fn write_to_clipboard(&self, item: ClipboardItem);
fn read_from_clipboard(&self) -> Option<ClipboardItem>; fn read_from_clipboard(&self) -> Option<ClipboardItem>;

View file

@ -70,8 +70,11 @@ impl super::Platform for Platform {
fn quit(&self) {} fn quit(&self) {}
fn prompt_for_paths(&self, _: super::PathPromptOptions) -> Option<Vec<std::path::PathBuf>> { fn prompt_for_paths(
None &self,
_: super::PathPromptOptions,
_: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
) {
} }
fn write_to_clipboard(&self, item: ClipboardItem) { fn write_to_clipboard(&self, item: ClipboardItem) {

View file

@ -29,19 +29,19 @@ pub struct OpenParams {
} }
fn open(settings: &Receiver<Settings>, ctx: &mut MutableAppContext) { fn open(settings: &Receiver<Settings>, ctx: &mut MutableAppContext) {
if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions { let settings = settings.clone();
files: true, ctx.prompt_for_paths(
directories: true, PathPromptOptions {
multiple: true, files: true,
}) { directories: true,
ctx.dispatch_global_action( multiple: true,
"workspace:open_paths", },
OpenParams { move |paths, ctx| {
paths, if let Some(paths) = paths {
settings: settings.clone(), ctx.dispatch_global_action("workspace:open_paths", OpenParams { paths, settings });
}, }
); },
} );
} }
fn open_paths(params: &OpenParams, app: &mut MutableAppContext) { fn open_paths(params: &OpenParams, app: &mut MutableAppContext) {