Replace Closed event with CloseRequested and Destroyed (#476)
* Replace Closed event with CloseRequested and Destroyed Implements #434 The existing Closed event had ambiguous meaning, both in name and in cross-platform behavior. Closed is now split into two more precise events: * CloseRequested - the window has been requested to close, most commonly by having clicked the window's close button. Whether or not you respond by closing the window is up to you. * Destroyed - the window has been destroyed, and can no longer be safely used. Most notably, now you can reliably implement classic patterns like prompting the user to save their work before closing, and have the opportunity to perform any necessary cleanup. Migrating to the new API is straightforward. In most cases, you can simply replace all existing usages of Closed with CloseRequested. For more information, see the example programs, particularly handling_close and multiwindow. iOS applications must replace all usages of Closed with Destroyed, and require no other changes.
This commit is contained in:
parent
42f0671531
commit
eadd9a19b2
21 changed files with 213 additions and 116 deletions
|
|
@ -335,8 +335,11 @@ impl EventsLoop {
|
|||
{
|
||||
let mut cleanup_needed = self.cleanup_needed.lock().unwrap();
|
||||
if *cleanup_needed {
|
||||
evq.state().get_mut(&self.store).cleanup();
|
||||
let pruned = evq.state().get_mut(&self.store).cleanup();
|
||||
*cleanup_needed = false;
|
||||
for wid in pruned {
|
||||
sink.send_event(::WindowEvent::Destroyed, wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
// process pending resize/refresh
|
||||
|
|
@ -355,7 +358,7 @@ impl EventsLoop {
|
|||
sink.send_event(::WindowEvent::Refresh, wid);
|
||||
}
|
||||
if closed {
|
||||
sink.send_event(::WindowEvent::Closed, wid);
|
||||
sink.send_event(::WindowEvent::CloseRequested, wid);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -262,16 +262,19 @@ impl WindowStore {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
pub fn cleanup(&mut self) -> Vec<WindowId> {
|
||||
let mut pruned = Vec::new();
|
||||
self.windows.retain(|w| {
|
||||
if *w.kill_switch.lock().unwrap() {
|
||||
// window is dead, cleanup
|
||||
pruned.push(make_wid(&w.surface));
|
||||
w.surface.destroy();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
pruned
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
|
|
|
|||
|
|
@ -245,15 +245,7 @@ impl EventsLoop {
|
|||
let window_id = mkwid(window);
|
||||
|
||||
if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
|
||||
callback(Event::WindowEvent { window_id, event: WindowEvent::Closed });
|
||||
|
||||
if let Some(_) = self.windows.lock().unwrap().remove(&WindowId(window)) {
|
||||
unsafe {
|
||||
(self.display.xlib.XDestroyWindow)(self.display.display, window);
|
||||
}
|
||||
self.display.check_errors()
|
||||
.expect("Failed to destroy window");
|
||||
}
|
||||
callback(Event::WindowEvent { window_id, event: WindowEvent::CloseRequested });
|
||||
} else if client_msg.message_type == self.dnd.atoms.enter {
|
||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
||||
let flags = client_msg.data.get_long(1);
|
||||
|
|
@ -430,11 +422,20 @@ impl EventsLoop {
|
|||
let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
|
||||
|
||||
let window = xev.window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
// In the event that the window's been destroyed without being dropped first, we
|
||||
// cleanup again here.
|
||||
self.windows.lock().unwrap().remove(&WindowId(window));
|
||||
|
||||
// Since all XIM stuff needs to happen from the same thread, we destroy the input
|
||||
// context here instead of when dropping the window.
|
||||
self.ime
|
||||
.borrow_mut()
|
||||
.remove_context(window)
|
||||
.expect("Failed to destroy input context");
|
||||
|
||||
callback(Event::WindowEvent { window_id, event: WindowEvent::Destroyed });
|
||||
}
|
||||
|
||||
ffi::Expose => {
|
||||
|
|
@ -1059,27 +1060,12 @@ impl Window {
|
|||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
if let (Some(windows), Some(display)) = (self.windows.upgrade(), self.display.upgrade()) {
|
||||
// It's possible for the Window object to outlive the actual window, so we need to
|
||||
// check for that, lest the program explode with BadWindow errors soon after this.
|
||||
let window_closed = windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&self.window.id())
|
||||
.is_none();
|
||||
if !window_closed { unsafe {
|
||||
let wm_protocols_atom = util::get_atom(&display, b"WM_PROTOCOLS\0")
|
||||
.expect("Failed to call XInternAtom (WM_PROTOCOLS)");
|
||||
let wm_delete_atom = util::get_atom(&display, b"WM_DELETE_WINDOW\0")
|
||||
.expect("Failed to call XInternAtom (WM_DELETE_WINDOW)");
|
||||
util::send_client_msg(
|
||||
&display,
|
||||
self.window.id().0,
|
||||
self.window.id().0,
|
||||
wm_protocols_atom,
|
||||
None,
|
||||
(wm_delete_atom as _, ffi::CurrentTime as _, 0, 0, 0),
|
||||
).expect("Failed to send window deletion message");
|
||||
} }
|
||||
if let Some(_) = windows.lock().unwrap().remove(&self.window.id()) {
|
||||
unsafe {
|
||||
(display.xlib.XDestroyWindow)(display.display, self.window.id().0);
|
||||
display.check_errors().expect("Failed to destroy window");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue