diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index cdd40550..25277861 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -329,6 +329,10 @@ impl WindowImpl for BaseviewWindow { // Now we can get the actual dpi of the window. self.initialize_window_dpi(window)?; + if !self.shared_state.parented.get() { + window.center_on_primary_monitor()?; + } + let drop_target = ComObject::new(DropTarget::new(Rc::downgrade(window_state), window)); self._drop_target.set(Some(drop_target.clone())); diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 72d855b7..4b0abaf4 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -20,9 +20,10 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ GetFocus, ReleaseCapture, SetCapture, SetFocus, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT, }; use windows_sys::Win32::UI::WindowsAndMessaging::{ - DestroyWindow, GetWindowLongPtrW, GetWindowLongW, KillTimer, PostMessageW, SetParent, SetTimer, - SetWindowLongPtrW, SetWindowPos, ShowWindow, GWLP_USERDATA, GWL_EXSTYLE, GWL_STYLE, - SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_LONG_PTR_INDEX, WM_USER, + DestroyWindow, GetSystemMetrics, GetWindowLongPtrW, GetWindowLongW, GetWindowRect, KillTimer, + PostMessageW, SetParent, SetTimer, SetWindowLongPtrW, SetWindowPos, ShowWindow, GWLP_USERDATA, + GWL_EXSTYLE, GWL_STYLE, SM_CXSCREEN, SM_CYSCREEN, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, + SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_LONG_PTR_INDEX, WM_USER, }; pub(crate) const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1; @@ -189,6 +190,41 @@ impl HWnd { self.resize_nc_and_activate(rect.size()) } + /// Centers the window on the primary monitor, preserving its current non-client size. + pub fn center_on_primary_monitor(&self) -> Result<()> { + let mut rect = Rect::EMPTY; + // SAFETY: This type guarantees the HWND is safe to use. + if unsafe { GetWindowRect(self.as_raw(), &mut rect.0) } == 0 { + return Err(Error::from_thread()); + } + + let size = rect.size(); + let width = i32::try_from(size.width).unwrap_or(i32::MAX); + let height = i32::try_from(size.height).unwrap_or(i32::MAX); + + // SAFETY: GetSystemMetrics is always safe to call. + let x = unsafe { GetSystemMetrics(SM_CXSCREEN) }.saturating_sub(width).div_euclid(2); + let y = unsafe { GetSystemMetrics(SM_CYSCREEN) }.saturating_sub(height).div_euclid(2); + + let result = unsafe { + SetWindowPos( + self.as_raw(), + null_mut(), // Ignored by SWP_NOZORDER + x, + y, + 0, // Ignored by SWP_NOSIZE + 0, // Ignored by SWP_NOSIZE + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE, + ) + }; + + if result == 0 { + return Err(Error::from_thread()); + } + + Ok(()) + } + /// Returns true if the window was previously visible, false otherwise. pub fn show_and_activate(&self) -> bool { let result = unsafe { ShowWindow(self.as_raw(), SW_SHOW) };