Merge pull request #1791 from AyberkA0/fix/1758-cursor-hotspot
Fix #1758: keep the custom title bar window borderless on Windows
This commit is contained in:
commit
a1eb4fc090
|
|
@ -45,6 +45,7 @@ mod printing;
|
|||
#[cfg(all(windows, not(target_vendor = "win7")))]
|
||||
mod system_fonts;
|
||||
mod window;
|
||||
mod window_frame;
|
||||
mod xwayland;
|
||||
|
||||
use vectorcraft_engine::Session;
|
||||
|
|
@ -62,6 +63,8 @@ struct App {
|
|||
graphics_lost: bool,
|
||||
/// Frames the UI has run (see [`end_before_teardown`]).
|
||||
frames: u64,
|
||||
/// Keeps the custom title bar's window borderless (Windows, #1758).
|
||||
window_frame: Option<window_frame::WindowFrameFn>,
|
||||
}
|
||||
|
||||
impl eframe::App for App {
|
||||
|
|
@ -72,6 +75,11 @@ impl eframe::App for App {
|
|||
file_access::unconfined(|| self.logic_frame(ctx, frame));
|
||||
}
|
||||
fn raw_input_hook(&mut self, _ctx: &egui::Context, raw: &mut egui::RawInput) {
|
||||
// winit gives the window its caption back when it changes the window's flags (maximize,
|
||||
// restore, …): take it off again before the frame is laid out (#1758).
|
||||
if let Some(window_frame) = self.window_frame.as_mut() {
|
||||
window_frame();
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if self.app.services.native_menu.is_some() {
|
||||
mac_menu::raw_input_hook(raw);
|
||||
|
|
@ -858,7 +866,9 @@ fn main() -> std::process::ExitCode {
|
|||
// The first frame is due from now (`gpu::watch_first_frame`).
|
||||
#[cfg(feature = "wgpu")]
|
||||
created.ready();
|
||||
Ok(Box::new(App { app, graphics_loss, graphics_lost: false, frames: 0 }))
|
||||
// The content and the pointer stay aligned on Windows (#1758).
|
||||
let window_frame = window_frame::service(cc, custom_titlebar);
|
||||
Ok(Box::new(App { app, graphics_loss, graphics_lost: false, frames: 0, window_frame }))
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
//! Keeps the custom title bar's undecorated window truly borderless on Windows (#1758).
|
||||
//!
|
||||
//! winit 0.30 gives an undecorated window the `WS_CAPTION | WS_BORDER` styles and hides the
|
||||
//! non-client area by spreading the client rect over it (`WM_NCCALCSIZE`). On some systems the
|
||||
//! desktop compositor still reserves that frame for the window surface: the content is drawn
|
||||
//! offset by the caption and border while the pointer keeps the un-offset client coordinates, so
|
||||
//! it no longer points at what is drawn under it (rust-windowing/winit#2419; PhotoCraft fixed the
|
||||
//! same in storytold/photocraft#2401). [`service`] strips the two styles and has Windows
|
||||
//! recalculate the frame; winit puts them back whenever it changes the window flags (maximize,
|
||||
//! restore, …), so the returned guard runs every frame.
|
||||
|
||||
/// Run once per frame; a no-op once the window is borderless.
|
||||
pub type WindowFrameFn = Box<dyn FnMut()>;
|
||||
|
||||
pub use platform::service;
|
||||
|
||||
#[cfg(all(windows, feature = "wgpu"))]
|
||||
mod platform {
|
||||
use eframe::wgpu::rwh::{HasWindowHandle, RawWindowHandle};
|
||||
use winsafe::{HWND, HwndPlace, POINT, SIZE, co};
|
||||
|
||||
/// A guard that keeps the window borderless each frame, or `None` with the system title bar
|
||||
/// (the window is decorated) or when its handle can't be found.
|
||||
pub fn service(cc: &eframe::CreationContext<'_>, custom_titlebar: bool) -> Option<super::WindowFrameFn> {
|
||||
if !custom_titlebar {
|
||||
return None;
|
||||
}
|
||||
let target = match cc.window_handle().ok()?.as_raw() {
|
||||
RawWindowHandle::Win32(h) => h.hwnd.get(),
|
||||
_ => return None,
|
||||
};
|
||||
let hwnd = find_window(target)?;
|
||||
strip(&hwnd);
|
||||
Some(Box::new(move || strip(&hwnd)))
|
||||
}
|
||||
|
||||
/// The top-level window with the given raw handle: `EnumWindows` hands out real `HWND`s, so
|
||||
/// this needs no `unsafe` to wrap a raw one.
|
||||
fn find_window(target: isize) -> Option<HWND> {
|
||||
let mut found = None;
|
||||
let _ = winsafe::EnumWindows(|hwnd| {
|
||||
if hwnd.ptr() as isize == target {
|
||||
found = Some(hwnd);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
found
|
||||
}
|
||||
|
||||
/// Removes `WS_CAPTION` (which includes `WS_BORDER`) and has Windows recalculate the frame;
|
||||
/// the sizing border stays, so edge resizing and snapping keep working.
|
||||
fn strip(hwnd: &HWND) {
|
||||
let style = hwnd.style();
|
||||
let without = style & !(co::WS::CAPTION | co::WS::BORDER);
|
||||
if without.raw() == style.raw() {
|
||||
return;
|
||||
}
|
||||
hwnd.set_style(without);
|
||||
let _ = hwnd.SetWindowPos(
|
||||
HwndPlace::None,
|
||||
POINT::new(),
|
||||
SIZE::new(),
|
||||
co::SWP::FRAMECHANGED | co::SWP::NOMOVE | co::SWP::NOSIZE | co::SWP::NOZORDER | co::SWP::NOACTIVATE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(windows, feature = "wgpu")))]
|
||||
mod platform {
|
||||
pub fn service(_cc: &eframe::CreationContext<'_>, _custom_titlebar: bool) -> Option<super::WindowFrameFn> {
|
||||
None
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue