Display prompt when trying to save a conflicting file
This commit is contained in:
@@ -2,7 +2,7 @@ use crate::{
|
||||
elements::ElementBox,
|
||||
executor,
|
||||
keymap::{self, Keystroke},
|
||||
platform::{self, WindowOptions},
|
||||
platform::{self, PromptLevel, WindowOptions},
|
||||
presenter::Presenter,
|
||||
util::{post_inc, timeout},
|
||||
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
|
||||
@@ -578,6 +578,31 @@ impl MutableAppContext {
|
||||
self.platform.set_menus(menus);
|
||||
}
|
||||
|
||||
pub fn prompt<F>(
|
||||
&self,
|
||||
window_id: usize,
|
||||
level: PromptLevel,
|
||||
msg: &str,
|
||||
answers: &[&str],
|
||||
done_fn: F,
|
||||
) where
|
||||
F: 'static + FnOnce(usize, &mut MutableAppContext),
|
||||
{
|
||||
let app = self.weak_self.as_ref().unwrap().upgrade().unwrap();
|
||||
let foreground = self.foreground.clone();
|
||||
let (_, window) = &self.presenters_and_platform_windows[&window_id];
|
||||
window.prompt(
|
||||
level,
|
||||
msg,
|
||||
answers,
|
||||
Box::new(move |answer| {
|
||||
foreground
|
||||
.spawn(async move { (done_fn)(answer, &mut *app.borrow_mut()) })
|
||||
.detach();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn prompt_for_paths<F>(&self, options: PathPromptOptions, done_fn: F)
|
||||
where
|
||||
F: 'static + FnOnce(Option<Vec<PathBuf>>, &mut MutableAppContext),
|
||||
@@ -1766,6 +1791,14 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||
&self.app.ctx.background
|
||||
}
|
||||
|
||||
pub fn prompt<F>(&self, level: PromptLevel, msg: &str, answers: &[&str], done_fn: F)
|
||||
where
|
||||
F: 'static + FnOnce(usize, &mut MutableAppContext),
|
||||
{
|
||||
self.app
|
||||
.prompt(self.window_id, level, msg, answers, done_fn)
|
||||
}
|
||||
|
||||
pub fn prompt_for_paths<F>(&self, options: PathPromptOptions, done_fn: F)
|
||||
where
|
||||
F: 'static + FnOnce(Option<Vec<PathBuf>>, &mut MutableAppContext),
|
||||
|
||||
@@ -24,7 +24,7 @@ pub mod color;
|
||||
pub mod json;
|
||||
pub mod keymap;
|
||||
mod platform;
|
||||
pub use platform::{Event, PathPromptOptions};
|
||||
pub use platform::{Event, PathPromptOptions, PromptLevel};
|
||||
pub use presenter::{
|
||||
AfterLayoutContext, Axis, DebugContext, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint, Vector2FExt,
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::{
|
||||
platform::{self, Event, WindowContext},
|
||||
Scene,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
|
||||
@@ -26,7 +27,8 @@ use objc::{
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
use smol::Timer;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
cell::{Cell, RefCell},
|
||||
convert::TryInto,
|
||||
ffi::c_void,
|
||||
mem, ptr,
|
||||
rc::{Rc, Weak},
|
||||
@@ -272,6 +274,42 @@ impl platform::Window for Window {
|
||||
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
|
||||
self.0.as_ref().borrow_mut().close_callback = Some(callback);
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
&self,
|
||||
level: platform::PromptLevel,
|
||||
msg: &str,
|
||||
answers: &[&str],
|
||||
done_fn: Box<dyn FnOnce(usize)>,
|
||||
) {
|
||||
unsafe {
|
||||
let alert: id = msg_send![class!(NSAlert), alloc];
|
||||
let alert: id = msg_send![alert, init];
|
||||
let alert_style = match level {
|
||||
platform::PromptLevel::Info => 1,
|
||||
platform::PromptLevel::Warning => 0,
|
||||
platform::PromptLevel::Critical => 2,
|
||||
};
|
||||
let _: () = msg_send![alert, setAlertStyle: alert_style];
|
||||
let _: () = msg_send![alert, setMessageText: ns_string(msg)];
|
||||
for (ix, answer) in answers.into_iter().enumerate() {
|
||||
let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
|
||||
let _: () = msg_send![button, setTag: ix as NSInteger];
|
||||
}
|
||||
let done_fn = Cell::new(Some(done_fn));
|
||||
let block = ConcreteBlock::new(move |answer: NSInteger| {
|
||||
if let Some(done_fn) = done_fn.take() {
|
||||
(done_fn)(answer.try_into().unwrap());
|
||||
}
|
||||
});
|
||||
let block = block.copy();
|
||||
let _: () = msg_send![
|
||||
alert,
|
||||
beginSheetModalForWindow: self.0.borrow().native_window
|
||||
completionHandler: block
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl platform::WindowContext for Window {
|
||||
@@ -515,3 +553,7 @@ async fn synthetic_drag(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn ns_string(string: &str) -> id {
|
||||
NSString::alloc(nil).init_str(string).autorelease()
|
||||
}
|
||||
|
||||
@@ -71,6 +71,13 @@ pub trait Window: WindowContext {
|
||||
fn on_event(&mut self, callback: Box<dyn FnMut(Event)>);
|
||||
fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn WindowContext)>);
|
||||
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
|
||||
fn prompt(
|
||||
&self,
|
||||
level: PromptLevel,
|
||||
msg: &str,
|
||||
answers: &[&str],
|
||||
done_fn: Box<dyn FnOnce(usize)>,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait WindowContext {
|
||||
@@ -90,6 +97,12 @@ pub struct PathPromptOptions {
|
||||
pub multiple: bool,
|
||||
}
|
||||
|
||||
pub enum PromptLevel {
|
||||
Info,
|
||||
Warning,
|
||||
Critical,
|
||||
}
|
||||
|
||||
pub trait FontSystem: Send + Sync {
|
||||
fn load_family(&self, name: &str) -> anyhow::Result<Vec<FontId>>;
|
||||
fn select_font(
|
||||
|
||||
@@ -163,6 +163,8 @@ impl super::Window for Window {
|
||||
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
|
||||
self.close_handlers.push(callback);
|
||||
}
|
||||
|
||||
fn prompt(&self, _: crate::PromptLevel, _: &str, _: &[&str], _: Box<dyn FnOnce(usize)>) {}
|
||||
}
|
||||
|
||||
pub(crate) fn platform() -> Platform {
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::{
|
||||
use futures_core::{future::LocalBoxFuture, Future};
|
||||
use gpui::{
|
||||
color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext,
|
||||
ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, PathPromptOptions, View,
|
||||
ViewContext, ViewHandle, WeakModelHandle,
|
||||
ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, PathPromptOptions,
|
||||
PromptLevel, View, ViewContext, ViewHandle, WeakModelHandle,
|
||||
};
|
||||
use log::error;
|
||||
pub use pane::*;
|
||||
@@ -573,15 +573,37 @@ impl Workspace {
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if item.has_conflict(ctx.as_ref()) {
|
||||
const CONFLICT_MESSAGE: &'static str = "This file has changed on disk since you started editing it. Do you want to overwrite it?";
|
||||
|
||||
let task = item.save(None, ctx.as_mut());
|
||||
ctx.spawn(task, |_, result, _| {
|
||||
if let Err(e) = result {
|
||||
error!("failed to save item: {:?}, ", e);
|
||||
}
|
||||
})
|
||||
.detach()
|
||||
let handle = ctx.handle();
|
||||
ctx.prompt(
|
||||
PromptLevel::Warning,
|
||||
CONFLICT_MESSAGE,
|
||||
&["Overwrite", "Cancel"],
|
||||
move |answer, ctx| {
|
||||
if answer == 0 {
|
||||
handle.update(ctx, move |_, ctx| {
|
||||
let task = item.save(None, ctx.as_mut());
|
||||
ctx.spawn(task, |_, result, _| {
|
||||
if let Err(e) = result {
|
||||
error!("failed to save item: {:?}, ", e);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let task = item.save(None, ctx.as_mut());
|
||||
ctx.spawn(task, |_, result, _| {
|
||||
if let Err(e) = result {
|
||||
error!("failed to save item: {:?}, ", e);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user