gpui: Fix window display on Windows (#18705)

- Closes #18610


This PR addresses the same issue as PR #18578. After a full day of
research and testing, I believe I’ve found the best solution to resolve
this issue. With this PR, the window creation behavior on Windows
becomes more consistent with macOS:

- When `params.show` is `true`: The window is created and immediately
displayed.
- When `params.show` is `false`: The window is created but remains
hidden until the first call to `activate_window`.

As I mentioned in #18578, `winit` creates hidden windows by setting the
window's `exstyle` to `WS_EX_NOACTIVATE | WS_EX_TRANSPARENT |
WS_EX_LAYERED | WS_EX_TOOLWINDOW`, which is different from the method
used in this PR. Here, the window is created with normal parameters, but
we do not call `ShowWindow` so the window is not shown.

I'm not sure why `winit` doesn't use a smilliar approach like this PR to
create hidden windows. My guess is that `winit` is creating this hidden
window to function as a "DispatchWindow" — serving a purpose similar to
`WindowsPlatform` in `zed`. To ensure the window stays hidden even if
`ShowWindow` is called, they use the `exstyle` approach.

With the method used in this PR, my initial tests haven't revealed any
issues.



Release Notes:

- N/A
This commit is contained in:
张小白 2024-10-10 19:09:50 +08:00 committed by GitHub
parent b75532fad7
commit 7c306a5a0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -52,6 +52,7 @@ pub struct WindowsWindowState {
pub display: WindowsDisplay, pub display: WindowsDisplay,
fullscreen: Option<StyleAndBounds>, fullscreen: Option<StyleAndBounds>,
initial_placement: Option<WINDOWPLACEMENT>,
hwnd: HWND, hwnd: HWND,
} }
@ -97,6 +98,7 @@ impl WindowsWindowState {
let system_settings = WindowsSystemSettings::new(display); let system_settings = WindowsSystemSettings::new(display);
let nc_button_pressed = None; let nc_button_pressed = None;
let fullscreen = None; let fullscreen = None;
let initial_placement = None;
Ok(Self { Ok(Self {
origin, origin,
@ -114,6 +116,7 @@ impl WindowsWindowState {
nc_button_pressed, nc_button_pressed,
display, display,
fullscreen, fullscreen,
initial_placement,
hwnd, hwnd,
}) })
} }
@ -231,6 +234,14 @@ impl WindowsWindowStatePtr {
main_receiver: context.main_receiver.clone(), main_receiver: context.main_receiver.clone(),
})) }))
} }
fn set_window_placement(&self) -> Result<()> {
let Some(placement) = self.state.borrow_mut().initial_placement.take() else {
return Ok(());
};
unsafe { SetWindowPlacement(self.hwnd, &placement)? };
Ok(())
}
} }
#[derive(Default)] #[derive(Default)]
@ -295,9 +306,6 @@ impl WindowsWindow {
WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
) )
}; };
if !params.show {
dwstyle |= WS_MINIMIZE;
}
let hinstance = get_module_handle(); let hinstance = get_module_handle();
let display = if let Some(display_id) = params.display_id { let display = if let Some(display_id) = params.display_id {
@ -336,36 +344,24 @@ impl WindowsWindow {
lpparam, lpparam,
) )
}; };
// We should call `?` on state_ptr first, then call `?` on raw_hwnd. // We should call `?` on state_ptr first, then call `?` on hwnd.
// Or, we will lose the error info reported by `WindowsWindowState::new` // Or, we will lose the error info reported by `WindowsWindowState::new`
let state_ptr = context.inner.take().unwrap()?; let state_ptr = context.inner.take().unwrap()?;
let raw_hwnd = creation_result?; let hwnd = creation_result?;
register_drag_drop(state_ptr.clone())?; register_drag_drop(state_ptr.clone())?;
unsafe { state_ptr.state.borrow_mut().border_offset.update(hwnd)?;
let mut placement = WINDOWPLACEMENT { let placement = retrieve_window_placement(
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32, hwnd,
..Default::default() display,
}; params.bounds,
GetWindowPlacement(raw_hwnd, &mut placement)?; state_ptr.state.borrow().scale_factor,
// the bounds may be not inside the display state_ptr.state.borrow().border_offset,
let bounds = if display.check_given_bounds(params.bounds) { )?;
params.bounds
} else {
display.default_bounds()
};
let mut lock = state_ptr.state.borrow_mut();
let bounds = bounds.to_device_pixels(lock.scale_factor);
lock.border_offset.update(raw_hwnd)?;
placement.rcNormalPosition = calculate_window_rect(bounds, lock.border_offset);
drop(lock);
SetWindowPlacement(raw_hwnd, &placement)?;
}
if params.show { if params.show {
unsafe { ShowWindow(raw_hwnd, SW_SHOW).ok()? }; unsafe { SetWindowPlacement(hwnd, &placement)? };
} else { } else {
unsafe { ShowWindow(raw_hwnd, SW_HIDE).ok()? }; state_ptr.state.borrow_mut().initial_placement = Some(placement);
} }
Ok(Self(state_ptr)) Ok(Self(state_ptr))
@ -541,11 +537,18 @@ impl PlatformWindow for WindowsWindow {
fn activate(&self) { fn activate(&self) {
let hwnd = self.0.hwnd; let hwnd = self.0.hwnd;
unsafe { SetActiveWindow(hwnd).log_err() }; let this = self.0.clone();
unsafe { SetFocus(hwnd).log_err() }; self.0
// todo(windows) .executor
// crate `windows 0.56` reports true as Err .spawn(async move {
unsafe { SetForegroundWindow(hwnd).as_bool() }; this.set_window_placement().log_err();
unsafe { SetActiveWindow(hwnd).log_err() };
unsafe { SetFocus(hwnd).log_err() };
// todo(windows)
// crate `windows 0.56` reports true as Err
unsafe { SetForegroundWindow(hwnd).as_bool() };
})
.detach();
} }
fn is_active(&self) -> bool { fn is_active(&self) -> bool {
@ -1066,6 +1069,29 @@ fn calculate_client_rect(
} }
} }
fn retrieve_window_placement(
hwnd: HWND,
display: WindowsDisplay,
initial_bounds: Bounds<Pixels>,
scale_factor: f32,
border_offset: WindowBorderOffset,
) -> Result<WINDOWPLACEMENT> {
let mut placement = WINDOWPLACEMENT {
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
..Default::default()
};
unsafe { GetWindowPlacement(hwnd, &mut placement)? };
// the bounds may be not inside the display
let bounds = if display.check_given_bounds(initial_bounds) {
initial_bounds
} else {
display.default_bounds()
};
let bounds = bounds.to_device_pixels(scale_factor);
placement.rcNormalPosition = calculate_window_rect(bounds, border_offset);
Ok(placement)
}
mod windows_renderer { mod windows_renderer {
use std::{num::NonZeroIsize, sync::Arc}; use std::{num::NonZeroIsize, sync::Arc};