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:
Francesca Frangipane 2018-04-24 16:20:40 -04:00 committed by GitHub
parent 42f0671531
commit eadd9a19b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 213 additions and 116 deletions

View file

@ -55,7 +55,7 @@ pub struct SavedWindowInfo {
pub style: LONG,
/// Window ex-style
pub ex_style: LONG,
/// Window position and size
/// Window position and size
pub rect: RECT,
}
@ -326,6 +326,13 @@ lazy_static! {
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
}
};
// Message sent by a `Window` when it wants to be destroyed by the main thread.
// WPARAM and LPARAM are unused.
pub static ref DESTROY_MSG_ID: u32 = {
unsafe {
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as *const i8)
}
};
}
// There's no parameters passed to the callback function, so it needs to get its context stashed
@ -386,17 +393,26 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
{
match msg {
winuser::WM_CLOSE => {
use events::WindowEvent::Closed;
use events::WindowEvent::CloseRequested;
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: Closed
event: CloseRequested
});
0
},
winuser::WM_DESTROY => {
use events::WindowEvent::Destroyed;
CONTEXT_STASH.with(|context_stash| {
let mut context_stash = context_stash.borrow_mut();
context_stash.as_mut().unwrap().windows.remove(&window);
});
winuser::DefWindowProcW(window, msg, wparam, lparam)
},
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: Destroyed
});
0
},
winuser::WM_PAINT => {
use events::WindowEvent::Refresh;
@ -922,7 +938,12 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
},
_ => {
winuser::DefWindowProcW(window, msg, wparam, lparam)
if msg == *DESTROY_MSG_ID {
winuser::DestroyWindow(window);
0
} else {
winuser::DefWindowProcW(window, msg, wparam, lparam)
}
}
}
}

View file

@ -11,7 +11,7 @@ use std::sync::Mutex;
use std::sync::mpsc::channel;
use std::cell::Cell;
use platform::platform::events_loop;
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
use platform::platform::EventsLoop;
use platform::platform::PlatformSpecificWindowBuilderAttributes;
use platform::platform::WindowId;
@ -47,14 +47,14 @@ unsafe impl Send for Window {}
unsafe impl Sync for Window {}
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
// The idea here is that we use the Adjust­Window­Rect­Ex function to calculate how much additional
// non-client area gets added due to the styles we passed. To make the math simple,
// we ask for a zero client rectangle, so that the resulting window is all non-client.
// And then we pass in the empty rectangle represented by the dot in the middle,
// and the Adjust­Window­Rect­Ex expands the rectangle in all dimensions.
// We see that it added ten pixels to the left, right, and bottom,
// The idea here is that we use the Adjust­Window­Rect­Ex function to calculate how much additional
// non-client area gets added due to the styles we passed. To make the math simple,
// we ask for a zero client rectangle, so that the resulting window is all non-client.
// And then we pass in the empty rectangle represented by the dot in the middle,
// and the Adjust­Window­Rect­Ex expands the rectangle in all dimensions.
// We see that it added ten pixels to the left, right, and bottom,
// and it added fifty pixels to the top.
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL {
let mut rc: RECT = mem::zeroed();
@ -67,7 +67,7 @@ unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> B
prc.right -= rc.right;
prc.bottom -= rc.bottom;
}
frc
}
@ -378,13 +378,13 @@ impl Window {
pub fn set_maximized(&self, maximized: bool) {
let mut window_state = self.window_state.lock().unwrap();
window_state.attributes.maximized = maximized;
window_state.attributes.maximized = maximized;
// we only maximized if we are not in fullscreen
if window_state.attributes.fullscreen.is_some() {
return;
}
let window = self.window.clone();
let window = self.window.clone();
unsafe {
// And because ShowWindow will resize the window
// We call it in the main thread
@ -620,9 +620,9 @@ impl Drop for Window {
#[inline]
fn drop(&mut self) {
unsafe {
// We are sending WM_CLOSE, and our callback will process this by calling DefWindowProcW,
// which in turn will send a WM_DESTROY.
winuser::PostMessageW(self.window.0, winuser::WM_CLOSE, 0, 0);
// The window must be destroyed from the same thread that created it, so we send a
// custom message to be handled by our callback to do the actual work.
winuser::PostMessageW(self.window.0, *DESTROY_MSG_ID, 0, 0);
}
}
}
@ -770,7 +770,7 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
}
let win = Window {
window: real_window,
window_state: window_state,
@ -784,7 +784,7 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
}
inserter.insert(win.window.0, win.window_state.clone());
Ok(win)
}
@ -824,7 +824,7 @@ impl Drop for ComInitialized {
}
}
thread_local!{
thread_local!{
static COM_INITIALIZED: ComInitialized = {
unsafe {
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED);
@ -871,7 +871,7 @@ mod taskbar {
fn MarkFullscreenWindow(
hwnd: HWND,
fFullscreen: BOOL,
) -> HRESULT,
) -> HRESULT,
});
}