Fix crash when closing windows

Nathan Sobo created

Change summary

gpui/src/app.rs                 | 11 +++++++++++
gpui/src/platform/mac/window.rs | 25 ++++++++++++++++++++++++-
gpui/src/platform/mod.rs        |  1 +
gpui/src/platform/test.rs       |  6 ++++++
4 files changed, 42 insertions(+), 1 deletion(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -758,6 +758,10 @@ impl MutableAppContext {
         (window_id, root_handle)
     }
 
+    pub fn remove_window(&mut self, window_id: usize) {
+        self.presenters_and_platform_windows.remove(&window_id);
+    }
+
     fn open_platform_window(&mut self, window_id: usize) {
         let mut window = self.platform.open_window(
             window_id,
@@ -814,6 +818,13 @@ impl MutableAppContext {
             }));
         }
 
+        {
+            let mut app = self.upgrade();
+            window.on_close(Box::new(move || {
+                app.update(|ctx| ctx.remove_window(window_id));
+            }));
+        }
+
         self.presenters_and_platform_windows
             .insert(window_id, (presenter.clone(), window));
 

gpui/src/platform/mac/window.rs 🔗

@@ -61,6 +61,7 @@ unsafe fn build_classes() {
             sel!(sendEvent:),
             send_event as extern "C" fn(&Object, Sel, id),
         );
+        decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
         decl.register()
     };
 
@@ -125,6 +126,7 @@ struct WindowState {
     native_window: id,
     event_callback: Option<Box<dyn FnMut(Event)>>,
     resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
+    close_callback: Option<Box<dyn FnOnce()>>,
     synthetic_drag_counter: usize,
     executor: Rc<executor::Foreground>,
     scene_to_render: Option<Scene>,
@@ -184,6 +186,7 @@ impl Window {
                 native_window,
                 event_callback: None,
                 resize_callback: None,
+                close_callback: None,
                 synthetic_drag_counter: 0,
                 executor,
                 scene_to_render: Default::default(),
@@ -262,6 +265,10 @@ impl platform::Window for Window {
     fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn platform::WindowContext)>) {
         self.0.as_ref().borrow_mut().resize_callback = Some(callback);
     }
+
+    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
+        self.0.as_ref().borrow_mut().close_callback = Some(callback);
+    }
 }
 
 impl platform::WindowContext for Window {
@@ -310,7 +317,7 @@ unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
 
 unsafe fn drop_window_state(object: &Object) {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    Rc::from_raw(raw as *mut WindowState);
+    Rc::from_raw(raw as *mut RefCell<WindowState>);
 }
 
 extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
@@ -371,6 +378,22 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
     }
 }
 
+extern "C" fn close_window(this: &Object, _: Sel) {
+    unsafe {
+        let window_state = get_window_state(this);
+        let close_callback = window_state
+            .as_ref()
+            .try_borrow_mut()
+            .ok()
+            .and_then(|mut window_state| window_state.close_callback.take());
+        if let Some(callback) = close_callback {
+            callback();
+        }
+
+        let () = msg_send![super(this, class!(NSWindow)), close];
+    }
+}
+
 extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
     let window_state = unsafe { get_window_state(this) };
     let window_state = window_state.as_ref().borrow();

gpui/src/platform/mod.rs 🔗

@@ -59,6 +59,7 @@ pub trait Dispatcher: Send + Sync {
 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()>);
 }
 
 pub trait WindowContext {

gpui/src/platform/test.rs 🔗

@@ -16,6 +16,7 @@ pub struct Window {
     current_scene: Option<crate::Scene>,
     event_handlers: Vec<Box<dyn FnMut(super::Event)>>,
     resize_handlers: Vec<Box<dyn FnMut(&mut dyn super::WindowContext)>>,
+    close_handlers: Vec<Box<dyn FnOnce()>>,
 }
 
 impl Platform {
@@ -92,6 +93,7 @@ impl Window {
             size,
             event_handlers: Vec::new(),
             resize_handlers: Vec::new(),
+            close_handlers: Vec::new(),
             scale_factor: 1.0,
             current_scene: None,
         }
@@ -130,6 +132,10 @@ impl super::Window for Window {
     fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn super::WindowContext)>) {
         self.resize_handlers.push(callback);
     }
+
+    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
+        self.close_handlers.push(callback);
+    }
 }
 
 pub fn platform() -> impl super::Platform {