T48: round 2 fix: at a new DPI her corner follows once the move that brought it is done, and only where her box would lie mostly on a display of that DPI; else Windows' rect stands, since between two displays side by side a corner kept always set her DPI back and forth; the tests move beside the module
This commit is contained in:
5 files changed
+357
-211
No files matched your search
+102
-208
@@ -1,31 +1,45 @@
|
||||
//! Ely's window on Windows: a bare popup owned by the main window, so it stays above it and hides with it, sized and moved as it moves.
|
||||
|
||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
sync::atomic::{AtomicIsize, Ordering},
|
||||
};
|
||||
|
||||
use anyhow::bail;
|
||||
use gpui::{Bounds, Pixels, Window};
|
||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
|
||||
Graphics::Gdi::{MONITOR_DEFAULTTONEAREST, MonitorFromRect},
|
||||
UI::{
|
||||
HiDpi::GetDpiForWindow,
|
||||
HiDpi::{GetDpiForMonitor, GetDpiForWindow, MDT_EFFECTIVE_DPI},
|
||||
WindowsAndMessaging::{
|
||||
CallWindowProcW, GWL_STYLE, GWLP_HWNDPARENT, GWLP_WNDPROC, GetWindowInfo,
|
||||
GetWindowLongPtrW, GetWindowRect, HWND_NOTOPMOST, MA_NOACTIVATE, PostMessageW,
|
||||
SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER,
|
||||
SetWindowLongPtrW, SetWindowPos, USER_DEFAULT_SCREEN_DPI, WINDOWINFO, WM_APP,
|
||||
WM_DPICHANGED, WM_MOUSEACTIVATE, WNDPROC, WS_CAPTION, WS_POPUP,
|
||||
GetWindowLongPtrW, HWND_NOTOPMOST, MA_NOACTIVATE, PostMessageW, SWP_FRAMECHANGED,
|
||||
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetWindowLongPtrW, SetWindowPos,
|
||||
USER_DEFAULT_SCREEN_DPI, WINDOWINFO, WM_APP, WM_DPICHANGED, WM_MOUSEACTIVATE, WNDPROC,
|
||||
WS_CAPTION, WS_POPUP,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{HEAD, ROOM, WIDTH, left, pane};
|
||||
|
||||
/// gpui's window procedure, which hers passes every message on to but a press's activation and her place.
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// gpui's window procedure, which hers passes every message on to but a press's activation and her places.
|
||||
static GPUI: AtomicIsize = AtomicIsize::new(0);
|
||||
|
||||
thread_local! {
|
||||
/// The bottom left corner her last place gave her, on the thread her window lives on.
|
||||
static CORNER: Cell<Option<(i32, i32)>> = const { Cell::new(None) };
|
||||
}
|
||||
|
||||
/// Her place, posted to her window: Windows sends a new size at once, which gpui hears only outside its own updates.
|
||||
const PLACE: u32 = WM_APP;
|
||||
/// Her corner at a new DPI, posted so it follows the whole move that brought the change.
|
||||
const KEEP: u32 = WM_APP + 1;
|
||||
const _: () = assert!(
|
||||
size_of::<WPARAM>() == 8,
|
||||
"a place packs two i32 into a word"
|
||||
@@ -91,10 +105,7 @@ impl Glue {
|
||||
|
||||
/// Her box at `own` scale, its bottom on `pane` at `scale` over the main window's `client` rect: its place and size in pixels.
|
||||
fn stand(client: RECT, scale: f32, own: f32, pane: Bounds<Pixels>) -> ((i32, i32), (i32, i32)) {
|
||||
let size = (
|
||||
(WIDTH * own).round() as i32,
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
);
|
||||
let size = sized(own);
|
||||
let (x, y) = (f32::from(pane.origin.x), f32::from(pane.origin.y));
|
||||
let at = (
|
||||
client.left + ((x + left(pane.size.width)) * scale).round() as i32,
|
||||
@@ -103,7 +114,15 @@ fn stand(client: RECT, scale: f32, own: f32, pane: Bounds<Pixels>) -> ((i32, i32
|
||||
(at, size)
|
||||
}
|
||||
|
||||
/// Her window's procedure: a press does not activate her window, she takes her place, and at a new DPI keeps her corner; all else is gpui's.
|
||||
/// Her box's size in pixels at `own` scale.
|
||||
fn sized(own: f32) -> (i32, i32) {
|
||||
(
|
||||
(WIDTH * own).round() as i32,
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Her window's procedure: a press does not activate her window, she takes her places, and all else is gpui's.
|
||||
unsafe extern "system" fn inactive(
|
||||
window: HWND,
|
||||
message: u32,
|
||||
@@ -115,40 +134,88 @@ unsafe extern "system" fn inactive(
|
||||
WM_MOUSEACTIVATE => MA_NOACTIVATE as LRESULT,
|
||||
PLACE => {
|
||||
let ((x, y), (width, height)) = (unpack(wparam), unpack(lparam as WPARAM));
|
||||
let flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
||||
let null = std::ptr::null_mut();
|
||||
if unsafe { SetWindowPos(window, null, x, y, width, height, flags) } == 0 {
|
||||
log::error!("pet: her window does not move to {x},{y}");
|
||||
}
|
||||
CORNER.set(Some((x, y + height)));
|
||||
let rect = RECT {
|
||||
left: x,
|
||||
top: y,
|
||||
right: x + width,
|
||||
bottom: y + height,
|
||||
};
|
||||
move_to(window, rect);
|
||||
0
|
||||
}
|
||||
WM_DPICHANGED => {
|
||||
// gpui takes the rect Windows suggests, as a move to another display brings.
|
||||
let (mut before, mut after) = (RECT::default(), RECT::default());
|
||||
let read = unsafe { GetWindowRect(window, &mut before) };
|
||||
// gpui takes the rect Windows suggests; her corner follows once the move is done.
|
||||
let answer = unsafe { CallWindowProcW(gpui, window, message, wparam, lparam) };
|
||||
if read == 0 || unsafe { GetWindowRect(window, &mut after) } == 0 {
|
||||
log::error!("pet: her window's place does not read at a new DPI");
|
||||
return answer;
|
||||
}
|
||||
let top = before.bottom - (after.bottom - after.top);
|
||||
let flags = SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
|
||||
let null = std::ptr::null_mut();
|
||||
if unsafe { SetWindowPos(window, null, before.left, top, 0, 0, flags) } == 0 {
|
||||
log::error!("pet: her window does not keep its corner at a new DPI");
|
||||
} else {
|
||||
log::debug!(
|
||||
"pet: at a new DPI Ely keeps her corner at {},{}",
|
||||
before.left,
|
||||
before.bottom
|
||||
);
|
||||
if unsafe { PostMessageW(window, KEEP, wparam & 0xFFFF, 0) } == 0 {
|
||||
log::error!("pet: her corner does not post at a new DPI");
|
||||
}
|
||||
answer
|
||||
}
|
||||
KEEP => {
|
||||
let dpi = wparam as u32;
|
||||
let Some(corner) = CORNER.get() else {
|
||||
log::debug!("pet: at {dpi} DPI Ely has no place to keep yet");
|
||||
return 0;
|
||||
};
|
||||
match keeping(corner, dpi, display_dpi) {
|
||||
Ok(Some(kept)) => {
|
||||
move_to(window, kept);
|
||||
log::debug!("pet: at {dpi} DPI Ely keeps her corner at {corner:?}");
|
||||
}
|
||||
Ok(None) => log::debug!(
|
||||
"pet: at {dpi} DPI Ely stands where Windows suggests, as on her corner she would lie on a display of another"
|
||||
),
|
||||
Err(error) => log::error!("{error:#}"),
|
||||
}
|
||||
0
|
||||
}
|
||||
_ => unsafe { CallWindowProcW(gpui, window, message, wparam, lparam) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves her window to `rect`.
|
||||
fn move_to(window: HWND, rect: RECT) {
|
||||
let (width, height) = (rect.right - rect.left, rect.bottom - rect.top);
|
||||
let flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
||||
let null = std::ptr::null_mut();
|
||||
if unsafe { SetWindowPos(window, null, rect.left, rect.top, width, height, flags) } == 0 {
|
||||
log::error!(
|
||||
"pet: her window does not move to {},{}",
|
||||
rect.left,
|
||||
rect.top
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Her box at `dpi` on the bottom left `corner` her place gave her, if it lies mostly on a display of that DPI, as `dpi_of` reads it; else none, and Windows' rect stands, which sets off no other change.
|
||||
fn keeping(
|
||||
(left, bottom): (i32, i32),
|
||||
dpi: u32,
|
||||
dpi_of: impl Fn(&RECT) -> anyhow::Result<u32>,
|
||||
) -> anyhow::Result<Option<RECT>> {
|
||||
let (width, height) = sized(dpi as f32 / USER_DEFAULT_SCREEN_DPI as f32);
|
||||
let kept = RECT {
|
||||
left,
|
||||
top: bottom - height,
|
||||
right: left + width,
|
||||
bottom,
|
||||
};
|
||||
Ok((dpi_of(&kept)? == dpi).then_some(kept))
|
||||
}
|
||||
|
||||
/// The DPI of the display under most of `rect`, which Windows gives a window there.
|
||||
fn display_dpi(rect: &RECT) -> anyhow::Result<u32> {
|
||||
let monitor = unsafe { MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST) };
|
||||
let (mut x, mut y) = (0, 0);
|
||||
let read = unsafe { GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut x, &mut y) };
|
||||
anyhow::ensure!(
|
||||
read == 0,
|
||||
"pet: the display under her box gives no DPI: {read:#x}"
|
||||
);
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn pack((low, high): (i32, i32)) -> WPARAM {
|
||||
low as u32 as WPARAM | (high as u32 as WPARAM) << 32
|
||||
}
|
||||
@@ -166,176 +233,3 @@ fn native(window: &Window) -> anyhow::Result<HWND> {
|
||||
};
|
||||
Ok(win32.hwnd.get() as HWND)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::{point, px, size};
|
||||
use windows_sys::{
|
||||
Win32::UI::WindowsAndMessaging::{
|
||||
CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, MSG,
|
||||
PM_REMOVE, PeekMessageW, SendMessageW, WS_EX_TOOLWINDOW, WS_EX_TOPMOST,
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
},
|
||||
w,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A hidden window of `style`, at the size Windows picks, as gpui asks for its windows.
|
||||
fn window(style: u32, extended: u32) -> HWND {
|
||||
let window = unsafe {
|
||||
CreateWindowExW(
|
||||
extended,
|
||||
w!("STATIC"),
|
||||
w!(""),
|
||||
style,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null(),
|
||||
)
|
||||
};
|
||||
assert!(!window.is_null(), "a test window");
|
||||
window
|
||||
}
|
||||
|
||||
fn info(window: HWND) -> WINDOWINFO {
|
||||
let mut info = WINDOWINFO {
|
||||
cbSize: size_of::<WINDOWINFO>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
assert_ne!(unsafe { GetWindowInfo(window, &mut info) }, 0, "its info");
|
||||
info
|
||||
}
|
||||
|
||||
/// gpui's procedure as far as these tests need it: at a new DPI it takes the rect Windows suggests.
|
||||
unsafe extern "system" fn standin(
|
||||
window: HWND,
|
||||
message: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
if message == WM_DPICHANGED {
|
||||
let rect = unsafe { *(lparam as *const RECT) };
|
||||
let (width, height) = (rect.right - rect.left, rect.bottom - rect.top);
|
||||
let flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
||||
let null = std::ptr::null_mut();
|
||||
unsafe { SetWindowPos(window, null, rect.left, rect.top, width, height, flags) };
|
||||
return 0;
|
||||
}
|
||||
unsafe { DefWindowProcW(window, message, wparam, lparam) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn her_bottom_stands_on_the_panes_top_edge_whatever_the_two_windows_dpi() {
|
||||
let client = RECT {
|
||||
left: 100,
|
||||
top: 20,
|
||||
right: 1300,
|
||||
bottom: 900,
|
||||
};
|
||||
let pane = Bounds::new(point(px(10.), px(8.)), size(px(800.), px(600.)));
|
||||
for (scale, own) in [(1.0, 1.0), (1.0, 1.5), (1.5, 1.0), (2.0, 1.25)] {
|
||||
let ((x, y), (width, height)) = stand(client, scale, own, pane);
|
||||
let own_box = (
|
||||
(WIDTH * own).round() as i32,
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
);
|
||||
let corner = (
|
||||
100 + ((10.0 + left(px(800.))) * scale).round() as i32,
|
||||
20 + (8.0 * scale).round() as i32,
|
||||
);
|
||||
assert_eq!(
|
||||
((width, height), (x, y + height)),
|
||||
(own_box, corner),
|
||||
"the main window at {scale}, hers at {own}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edge() {
|
||||
let main = window(WS_OVERLAPPEDWINDOW, 0);
|
||||
// gpui's popup: style 0, an overlapped window, which Windows gives a caption.
|
||||
let pet = window(0, WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
|
||||
assert_eq!(
|
||||
info(pet).dwStyle & WS_CAPTION,
|
||||
WS_CAPTION,
|
||||
"framed as gpui makes it"
|
||||
);
|
||||
unsafe { SetWindowLongPtrW(pet, GWLP_WNDPROC, standin as *const () as isize) };
|
||||
let glue = Glue::hold(pet, main).expect("held");
|
||||
let pane = Bounds::new(point(px(0.), px(0.)), size(px(380.), px(280.)));
|
||||
glue.put(1.0, pane).expect("placed");
|
||||
let made = info(pet).rcWindow;
|
||||
let mut message = MSG::default();
|
||||
while unsafe { PeekMessageW(&mut message, pet, 0, 0, PM_REMOVE) } != 0 {
|
||||
unsafe { DispatchMessageW(&message) };
|
||||
}
|
||||
let (her, under) = (info(pet), info(main));
|
||||
let own = unsafe { GetDpiForWindow(pet) } as f32 / 96.0;
|
||||
// A new DPI, whose rect Windows suggests 30 and 40 pixels off, 100 by 60.
|
||||
let at = her.rcWindow;
|
||||
let suggested = RECT {
|
||||
left: at.left + 30,
|
||||
top: at.top + 40,
|
||||
right: at.left + 130,
|
||||
bottom: at.top + 100,
|
||||
};
|
||||
let pointer = &suggested as *const RECT as LPARAM;
|
||||
unsafe { SendMessageW(pet, WM_DPICHANGED, 144 | 144 << 16, pointer) };
|
||||
let later = info(pet).rcWindow;
|
||||
unsafe {
|
||||
DestroyWindow(pet);
|
||||
DestroyWindow(main);
|
||||
}
|
||||
let (outer, inner) = (her.rcWindow, her.rcClient);
|
||||
let frame = (
|
||||
inner.left - outer.left,
|
||||
inner.top - outer.top,
|
||||
outer.right - inner.right,
|
||||
outer.bottom - inner.bottom,
|
||||
);
|
||||
let drawn = (outer.right - outer.left, outer.bottom - outer.top);
|
||||
let own_box = (
|
||||
(WIDTH * own).round() as i32,
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
);
|
||||
let edge = (
|
||||
under.rcClient.left + left(px(380.)).round() as i32,
|
||||
under.rcClient.top,
|
||||
);
|
||||
let kept = (
|
||||
later.left,
|
||||
later.bottom,
|
||||
later.right - later.left,
|
||||
later.bottom - later.top,
|
||||
);
|
||||
assert_eq!(
|
||||
(
|
||||
her.dwStyle & WS_CAPTION,
|
||||
frame,
|
||||
drawn,
|
||||
(outer.left, outer.bottom),
|
||||
kept
|
||||
),
|
||||
(
|
||||
0,
|
||||
(0, 0, 0, 0),
|
||||
own_box,
|
||||
edge,
|
||||
(outer.left, outer.bottom, 100, 60)
|
||||
),
|
||||
"bare, in her own box, her bottom on the pane's top edge, kept there at a new DPI"
|
||||
);
|
||||
assert_ne!(
|
||||
(made.right - made.left, made.bottom - made.top),
|
||||
own_box,
|
||||
"no size until her window takes the posted place"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
use gpui::{point, px, size};
|
||||
use windows_sys::{
|
||||
Win32::UI::WindowsAndMessaging::{
|
||||
CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, MSG,
|
||||
PM_REMOVE, PeekMessageW, SendMessageW, WS_EX_TOOLWINDOW, WS_EX_TOPMOST,
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
},
|
||||
w,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A hidden window of `style`, at the size Windows picks, as gpui asks for its windows.
|
||||
fn window(style: u32, extended: u32) -> HWND {
|
||||
let window = unsafe {
|
||||
CreateWindowExW(
|
||||
extended,
|
||||
w!("STATIC"),
|
||||
w!(""),
|
||||
style,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null(),
|
||||
)
|
||||
};
|
||||
assert!(!window.is_null(), "a test window");
|
||||
window
|
||||
}
|
||||
|
||||
/// Hands her window the messages posted to it, as the message loop does.
|
||||
fn deliver(window: HWND) {
|
||||
let mut message = MSG::default();
|
||||
while unsafe { PeekMessageW(&mut message, window, 0, 0, PM_REMOVE) } != 0 {
|
||||
unsafe { DispatchMessageW(&message) };
|
||||
}
|
||||
}
|
||||
|
||||
fn info(window: HWND) -> WINDOWINFO {
|
||||
let mut info = WINDOWINFO {
|
||||
cbSize: size_of::<WINDOWINFO>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
assert_ne!(unsafe { GetWindowInfo(window, &mut info) }, 0, "its info");
|
||||
info
|
||||
}
|
||||
|
||||
/// gpui's procedure as far as these tests need it: at a new DPI it takes the rect Windows suggests.
|
||||
unsafe extern "system" fn standin(
|
||||
window: HWND,
|
||||
message: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
if message == WM_DPICHANGED {
|
||||
let rect = unsafe { *(lparam as *const RECT) };
|
||||
let (width, height) = (rect.right - rect.left, rect.bottom - rect.top);
|
||||
let flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
||||
let null = std::ptr::null_mut();
|
||||
unsafe { SetWindowPos(window, null, rect.left, rect.top, width, height, flags) };
|
||||
return 0;
|
||||
}
|
||||
unsafe { DefWindowProcW(window, message, wparam, lparam) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn her_bottom_stands_on_the_panes_top_edge_whatever_the_two_windows_dpi() {
|
||||
let client = RECT {
|
||||
left: 100,
|
||||
top: 20,
|
||||
right: 1300,
|
||||
bottom: 900,
|
||||
};
|
||||
let pane = Bounds::new(point(px(10.), px(8.)), size(px(800.), px(600.)));
|
||||
for (scale, own) in [(1.0, 1.0), (1.0, 1.5), (1.5, 1.0), (2.0, 1.25)] {
|
||||
let ((x, y), (width, height)) = stand(client, scale, own, pane);
|
||||
let own_box = (
|
||||
(WIDTH * own).round() as i32,
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
);
|
||||
let corner = (
|
||||
100 + ((10.0 + left(px(800.))) * scale).round() as i32,
|
||||
20 + (8.0 * scale).round() as i32,
|
||||
);
|
||||
assert_eq!(
|
||||
((width, height), (x, y + height)),
|
||||
(own_box, corner),
|
||||
"the main window at {scale}, hers at {own}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Two displays split at 0, 144 DPI before and 96 after, across or down, giving a box the DPI of the one under most of it, as Windows does.
|
||||
fn displays(down: bool) -> impl Fn(&RECT) -> anyhow::Result<u32> {
|
||||
move |rect: &RECT| {
|
||||
let (from, to) = if down {
|
||||
(rect.top, rect.bottom)
|
||||
} else {
|
||||
(rect.left, rect.right)
|
||||
};
|
||||
Ok(if to.min(0) - from > to - from.max(0) {
|
||||
144
|
||||
} else {
|
||||
96
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Placing her box `rect` at `dpi` on `displays`, Windows' rect at each change scaled about its middle, which lands on the new display: the box she settles in and how many changes it took.
|
||||
fn settle(mut rect: RECT, mut dpi: u32, down: bool) -> (RECT, usize) {
|
||||
let dpi_of = displays(down);
|
||||
let corner = (rect.left, rect.bottom);
|
||||
for changes in 0..8 {
|
||||
let now = dpi_of(&rect).expect("a display");
|
||||
if now == dpi {
|
||||
return (rect, changes);
|
||||
}
|
||||
let grow = |from: i32, to: i32| {
|
||||
let length = ((to - from) as f64 * now as f64 / dpi as f64).round() as i32;
|
||||
let start = (from + to - length).div_euclid(2);
|
||||
(start, start + length)
|
||||
};
|
||||
let ((left, right), (top, bottom)) =
|
||||
(grow(rect.left, rect.right), grow(rect.top, rect.bottom));
|
||||
let after = RECT {
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
};
|
||||
rect = keeping(corner, now, &dpi_of)
|
||||
.expect("a display")
|
||||
.unwrap_or(after);
|
||||
dpi = now;
|
||||
}
|
||||
panic!("her DPI goes back and forth");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placing_her_across_two_displays_of_two_dpis_settles_after_one_change() {
|
||||
// 168 by 185 at 144 DPI, 70 pixels on the first display and 98 on the second.
|
||||
let across = RECT {
|
||||
left: -70,
|
||||
top: 315,
|
||||
right: 98,
|
||||
bottom: 500,
|
||||
};
|
||||
let (rect, changes) = settle(across, 144, false);
|
||||
assert_eq!(
|
||||
changes, 1,
|
||||
"Windows' rect stands where her corner would set her DPI back"
|
||||
);
|
||||
assert_eq!(displays(false)(&rect).expect("a display"), 96);
|
||||
// 112 by 123 at 96 DPI, her bottom 50 pixels down the second display.
|
||||
let down = RECT {
|
||||
left: 200,
|
||||
top: -73,
|
||||
right: 312,
|
||||
bottom: 50,
|
||||
};
|
||||
let (rect, changes) = settle(down, 96, true);
|
||||
assert_eq!(
|
||||
(changes, rect.left, rect.bottom),
|
||||
(1, 200, 50),
|
||||
"her corner kept on the edge"
|
||||
);
|
||||
assert_eq!(rect.bottom - rect.top, 185);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edge() {
|
||||
let main = window(WS_OVERLAPPEDWINDOW, 0);
|
||||
// gpui's popup: style 0, an overlapped window, which Windows gives a caption.
|
||||
let pet = window(0, WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
|
||||
assert_eq!(
|
||||
info(pet).dwStyle & WS_CAPTION,
|
||||
WS_CAPTION,
|
||||
"framed as gpui makes it"
|
||||
);
|
||||
unsafe { SetWindowLongPtrW(pet, GWLP_WNDPROC, standin as *const () as isize) };
|
||||
let glue = Glue::hold(pet, main).expect("held");
|
||||
let pane = Bounds::new(point(px(0.), px(0.)), size(px(380.), px(280.)));
|
||||
glue.put(1.0, pane).expect("placed");
|
||||
let made = info(pet).rcWindow;
|
||||
deliver(pet);
|
||||
let (her, under) = (info(pet), info(main));
|
||||
let own = unsafe { GetDpiForWindow(pet) } as f32 / 96.0;
|
||||
// New DPIs, each with a rect Windows suggests 30 and 40 pixels off, 100 by 60.
|
||||
let display = display_dpi(&her.rcWindow).expect("the display under her");
|
||||
let change = |dpi: u32| {
|
||||
let at = info(pet).rcWindow;
|
||||
let suggested = RECT {
|
||||
left: at.left + 30,
|
||||
top: at.top + 40,
|
||||
right: at.left + 130,
|
||||
bottom: at.top + 100,
|
||||
};
|
||||
let pointer = &suggested as *const RECT as LPARAM;
|
||||
unsafe { SendMessageW(pet, WM_DPICHANGED, (dpi | dpi << 16) as WPARAM, pointer) };
|
||||
let taken = info(pet).rcWindow;
|
||||
deliver(pet);
|
||||
(suggested, taken, info(pet).rcWindow)
|
||||
};
|
||||
let (_, _, kept) = change(display);
|
||||
let (suggested, taken, stood) = change(display + 48);
|
||||
unsafe {
|
||||
DestroyWindow(pet);
|
||||
DestroyWindow(main);
|
||||
}
|
||||
let (outer, inner) = (her.rcWindow, her.rcClient);
|
||||
let frame = (
|
||||
inner.left - outer.left,
|
||||
inner.top - outer.top,
|
||||
outer.right - inner.right,
|
||||
outer.bottom - inner.bottom,
|
||||
);
|
||||
let drawn = (outer.right - outer.left, outer.bottom - outer.top);
|
||||
let own_box = (
|
||||
(WIDTH * own).round() as i32,
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
);
|
||||
let edge = (
|
||||
under.rcClient.left + left(px(380.)).round() as i32,
|
||||
under.rcClient.top,
|
||||
);
|
||||
let rect = |rect: RECT| (rect.left, rect.top, rect.right, rect.bottom);
|
||||
assert_eq!(
|
||||
(
|
||||
her.dwStyle & WS_CAPTION,
|
||||
frame,
|
||||
drawn,
|
||||
(outer.left, outer.bottom),
|
||||
rect(kept)
|
||||
),
|
||||
(0, (0, 0, 0, 0), own_box, edge, rect(outer)),
|
||||
"bare, in her own box, her bottom on the pane's top edge, kept there at her display's DPI"
|
||||
);
|
||||
assert_eq!(
|
||||
(rect(taken), rect(stood)),
|
||||
(rect(suggested), rect(suggested)),
|
||||
"gpui takes Windows' rect, which stands where her corner lies on a display of another DPI"
|
||||
);
|
||||
assert_ne!(
|
||||
(made.right - made.left, made.bottom - made.top),
|
||||
own_box,
|
||||
"no size until her window takes the posted place"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user