Introduce ViewContext::on_window_should_close
This is a new callback that can be used to interrupt closing the window when the user has unsaved changes.
This commit is contained in:
parent
ca8ddcdeec
commit
06033d7fa9
4 changed files with 78 additions and 3 deletions
|
@ -780,6 +780,7 @@ type GlobalObservationCallback = Box<dyn FnMut(&mut MutableAppContext)>;
|
||||||
type ReleaseObservationCallback = Box<dyn FnOnce(&dyn Any, &mut MutableAppContext)>;
|
type ReleaseObservationCallback = Box<dyn FnOnce(&dyn Any, &mut MutableAppContext)>;
|
||||||
type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut MutableAppContext)>;
|
type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut MutableAppContext)>;
|
||||||
type DeserializeActionCallback = fn(json: &str) -> anyhow::Result<Box<dyn Action>>;
|
type DeserializeActionCallback = fn(json: &str) -> anyhow::Result<Box<dyn Action>>;
|
||||||
|
type WindowShouldCloseSubscriptionCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
|
||||||
|
|
||||||
pub struct MutableAppContext {
|
pub struct MutableAppContext {
|
||||||
weak_self: Option<rc::Weak<RefCell<Self>>>,
|
weak_self: Option<rc::Weak<RefCell<Self>>>,
|
||||||
|
@ -2004,6 +2005,12 @@ impl MutableAppContext {
|
||||||
Effect::ActionDispatchNotification { action_id } => {
|
Effect::ActionDispatchNotification { action_id } => {
|
||||||
self.handle_action_dispatch_notification_effect(action_id)
|
self.handle_action_dispatch_notification_effect(action_id)
|
||||||
}
|
}
|
||||||
|
Effect::WindowShouldCloseSubscription {
|
||||||
|
window_id,
|
||||||
|
callback,
|
||||||
|
} => {
|
||||||
|
self.handle_window_should_close_subscription_effect(window_id, callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.pending_notifications.clear();
|
self.pending_notifications.clear();
|
||||||
self.remove_dropped_entities();
|
self.remove_dropped_entities();
|
||||||
|
@ -2451,6 +2458,17 @@ impl MutableAppContext {
|
||||||
self.action_dispatch_observations.lock().extend(callbacks);
|
self.action_dispatch_observations.lock().extend(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_window_should_close_subscription_effect(
|
||||||
|
&mut self,
|
||||||
|
window_id: usize,
|
||||||
|
mut callback: WindowShouldCloseSubscriptionCallback,
|
||||||
|
) {
|
||||||
|
let mut app = self.upgrade();
|
||||||
|
if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
|
||||||
|
window.on_should_close(Box::new(move || app.update(|cx| callback(cx))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus(&mut self, window_id: usize, view_id: Option<usize>) {
|
pub fn focus(&mut self, window_id: usize, view_id: Option<usize>) {
|
||||||
if let Some(pending_focus_index) = self.pending_focus_index {
|
if let Some(pending_focus_index) = self.pending_focus_index {
|
||||||
self.pending_effects.remove(pending_focus_index);
|
self.pending_effects.remove(pending_focus_index);
|
||||||
|
@ -2828,6 +2846,10 @@ pub enum Effect {
|
||||||
ActionDispatchNotification {
|
ActionDispatchNotification {
|
||||||
action_id: TypeId,
|
action_id: TypeId,
|
||||||
},
|
},
|
||||||
|
WindowShouldCloseSubscription {
|
||||||
|
window_id: usize,
|
||||||
|
callback: WindowShouldCloseSubscriptionCallback,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Effect {
|
impl Debug for Effect {
|
||||||
|
@ -2921,6 +2943,10 @@ impl Debug for Effect {
|
||||||
.field("is_active", is_active)
|
.field("is_active", is_active)
|
||||||
.finish(),
|
.finish(),
|
||||||
Effect::RefreshWindows => f.debug_struct("Effect::FullViewRefresh").finish(),
|
Effect::RefreshWindows => f.debug_struct("Effect::FullViewRefresh").finish(),
|
||||||
|
Effect::WindowShouldCloseSubscription { window_id, .. } => f
|
||||||
|
.debug_struct("Effect::WindowShouldCloseSubscription")
|
||||||
|
.field("window_id", window_id)
|
||||||
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3346,6 +3372,25 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_window_should_close<F>(&mut self, mut callback: F)
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(&mut T, &mut ViewContext<T>) -> bool,
|
||||||
|
{
|
||||||
|
let window_id = self.window_id();
|
||||||
|
let view = self.weak_handle();
|
||||||
|
self.pending_effects
|
||||||
|
.push_back(Effect::WindowShouldCloseSubscription {
|
||||||
|
window_id,
|
||||||
|
callback: Box::new(move |cx| {
|
||||||
|
if let Some(view) = view.upgrade(cx) {
|
||||||
|
view.update(cx, |view, cx| callback(view, cx))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_model<S, F>(&mut self, build_model: F) -> ModelHandle<S>
|
pub fn add_model<S, F>(&mut self, build_model: F) -> ModelHandle<S>
|
||||||
where
|
where
|
||||||
S: Entity,
|
S: Entity,
|
||||||
|
|
|
@ -93,6 +93,7 @@ pub trait Window: WindowContext {
|
||||||
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
|
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
|
||||||
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
|
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
|
||||||
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
|
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
|
||||||
|
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
|
||||||
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
|
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
|
||||||
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
|
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
|
||||||
fn activate(&self);
|
fn activate(&self);
|
||||||
|
|
|
@ -81,6 +81,10 @@ unsafe fn build_classes() {
|
||||||
sel!(windowDidResignKey:),
|
sel!(windowDidResignKey:),
|
||||||
window_did_change_key_status as extern "C" fn(&Object, Sel, id),
|
window_did_change_key_status as extern "C" fn(&Object, Sel, id),
|
||||||
);
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowShouldClose:),
|
||||||
|
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
|
decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
|
||||||
decl.register()
|
decl.register()
|
||||||
};
|
};
|
||||||
|
@ -167,6 +171,7 @@ struct WindowState {
|
||||||
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
||||||
activate_callback: Option<Box<dyn FnMut(bool)>>,
|
activate_callback: Option<Box<dyn FnMut(bool)>>,
|
||||||
resize_callback: Option<Box<dyn FnMut()>>,
|
resize_callback: Option<Box<dyn FnMut()>>,
|
||||||
|
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
|
||||||
close_callback: Option<Box<dyn FnOnce()>>,
|
close_callback: Option<Box<dyn FnOnce()>>,
|
||||||
synthetic_drag_counter: usize,
|
synthetic_drag_counter: usize,
|
||||||
executor: Rc<executor::Foreground>,
|
executor: Rc<executor::Foreground>,
|
||||||
|
@ -242,6 +247,7 @@ impl Window {
|
||||||
native_window,
|
native_window,
|
||||||
event_callback: None,
|
event_callback: None,
|
||||||
resize_callback: None,
|
resize_callback: None,
|
||||||
|
should_close_callback: None,
|
||||||
close_callback: None,
|
close_callback: None,
|
||||||
activate_callback: None,
|
activate_callback: None,
|
||||||
synthetic_drag_counter: 0,
|
synthetic_drag_counter: 0,
|
||||||
|
@ -339,6 +345,10 @@ impl platform::Window for Window {
|
||||||
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
|
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
|
||||||
|
self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
|
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
|
||||||
self.0.as_ref().borrow_mut().close_callback = Some(callback);
|
self.0.as_ref().borrow_mut().close_callback = Some(callback);
|
||||||
}
|
}
|
||||||
|
@ -674,6 +684,19 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
let mut window_state_borrow = window_state.as_ref().borrow_mut();
|
||||||
|
if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
|
||||||
|
drop(window_state_borrow);
|
||||||
|
let should_close = callback();
|
||||||
|
window_state.borrow_mut().should_close_callback = Some(callback);
|
||||||
|
should_close as BOOL
|
||||||
|
} else {
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" fn close_window(this: &Object, _: Sel) {
|
extern "C" fn close_window(this: &Object, _: Sel) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let close_callback = {
|
let close_callback = {
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub struct Window {
|
||||||
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
|
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
|
||||||
resize_handlers: Vec<Box<dyn FnMut()>>,
|
resize_handlers: Vec<Box<dyn FnMut()>>,
|
||||||
close_handlers: Vec<Box<dyn FnOnce()>>,
|
close_handlers: Vec<Box<dyn FnOnce()>>,
|
||||||
|
should_close_handler: Option<Box<dyn FnMut() -> bool>>,
|
||||||
pub(crate) title: Option<String>,
|
pub(crate) title: Option<String>,
|
||||||
pub(crate) edited: bool,
|
pub(crate) edited: bool,
|
||||||
pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
|
pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
|
||||||
|
@ -186,9 +187,10 @@ impl Window {
|
||||||
fn new(size: Vector2F) -> Self {
|
fn new(size: Vector2F) -> Self {
|
||||||
Self {
|
Self {
|
||||||
size,
|
size,
|
||||||
event_handlers: Vec::new(),
|
event_handlers: Default::default(),
|
||||||
resize_handlers: Vec::new(),
|
resize_handlers: Default::default(),
|
||||||
close_handlers: Vec::new(),
|
close_handlers: Default::default(),
|
||||||
|
should_close_handler: Default::default(),
|
||||||
scale_factor: 1.0,
|
scale_factor: 1.0,
|
||||||
current_scene: None,
|
current_scene: None,
|
||||||
title: None,
|
title: None,
|
||||||
|
@ -264,6 +266,10 @@ impl super::Window for Window {
|
||||||
fn set_edited(&mut self, edited: bool) {
|
fn set_edited(&mut self, edited: bool) {
|
||||||
self.edited = edited;
|
self.edited = edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
|
||||||
|
self.should_close_handler = Some(callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn platform() -> Platform {
|
pub fn platform() -> Platform {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue