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-task",
"bindgen",
"block",
"cc",
"cocoa",
"core-foundation",

View file

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

View file

@ -5,7 +5,7 @@ use crate::{
platform::{self, WindowOptions},
presenter::Presenter,
util::post_inc,
AssetCache, AssetSource, ClipboardItem, FontCache, TextLayoutCache,
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
};
use anyhow::{anyhow, Result};
use async_std::sync::Condvar;
@ -570,6 +570,22 @@ impl MutableAppContext {
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>(
&mut self,
window_id: usize,

View file

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

View file

@ -40,7 +40,11 @@ pub trait Platform {
executor: Rc<executor::Foreground>,
) -> Box<dyn Window>;
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 write_to_clipboard(&self, item: ClipboardItem);
fn read_from_clipboard(&self) -> Option<ClipboardItem>;

View file

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

View file

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