Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect scale when setting window position during creation. #2296

Merged
merged 1 commit into from
Dec 1, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 70 additions & 66 deletions druid-shell/src/backend/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,32 +1359,14 @@ impl WindowBuilder {
present_strategy: self.present_strategy,
};

// TODO: pos_x and pos_y are only scaled for windows with parents. But they need to be
// scaled for windows without parents too.
let (mut pos_x, mut pos_y) = match self.position {
Some(pos) => (pos.x as i32, pos.y as i32),
None => (CW_USEDEFAULT, CW_USEDEFAULT),
};
let scale = Scale::new(1.0, 1.0);

let mut area = ScaledArea::default();
let (width, height) = self
.size
.map(|size| {
area = ScaledArea::from_dp(size, scale);
let size_px = area.size_px();
(size_px.width as i32, size_px.height as i32)
})
.unwrap_or((CW_USEDEFAULT, CW_USEDEFAULT));

let (hmenu, accels, has_menu) = match self.menu {
Some(menu) => {
let accels = menu.accels();
(menu.into_hmenu(), accels, true)
}
None => (0 as HMENU, None, false),
};

// TODO: Can we know the DPI of the screen where the window will be created ahead of time?
// Could probably make a better guess, e.g. if all screens have some non-1.0 ratio.
// For now we just use 1.0 and fix it after creation, but before showing the window.
// Unless we have a parent window, in which case we use its scale as our guess.
let mut scale = Scale::new(1.0, 1.0);
let (mut width, mut height) = (CW_USEDEFAULT, CW_USEDEFAULT);
let (mut pos_x, mut pos_y) = (CW_USEDEFAULT, CW_USEDEFAULT);
let mut parent_pos_dp = None; // When set, the pos is parent_pos_dp + self.position
let mut dwStyle = WS_OVERLAPPEDWINDOW;
let mut dwExStyle: DWORD = 0;
let mut focusable = true;
Expand All @@ -1397,29 +1379,41 @@ impl WindowBuilder {
WindowLevel::Tooltip(parent_window_handle)
| WindowLevel::DropDown(parent_window_handle)
| WindowLevel::Modal(parent_window_handle) => {
// Guess the scale will be the same as the parent's
scale = parent_window_handle.get_scale().unwrap_or_default();
parent_pos_dp = Some(parent_window_handle.get_position());
parent_hwnd = parent_window_handle.0.get_hwnd();
dwStyle = WS_POPUP;
dwExStyle = WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW;
focusable = false;
if let Some(point_in_window_coord) = self.position {
let screen_point = parent_window_handle.get_position()
+ point_in_window_coord.to_vec2();
let scaled_point = WindowBuilder::scale_sub_window_position(
screen_point,
parent_window_handle.get_scale(),
);
pos_x = scaled_point.x as i32;
pos_y = scaled_point.y as i32;
} else {
warn!("No position provided for subwindow!");
}
}
}
} else {
// Default window level
window_level = WindowLevel::AppWindow;
}

// Calculate the window position in pixels
if let Some(pos_dp) = self.position {
(pos_x, pos_y) = calculate_window_pos(parent_pos_dp, pos_dp, scale);
}

let mut area = ScaledArea::default();
// Calculate the window size in pixels
if let Some(size_dp) = self.size {
area = ScaledArea::from_dp(size_dp, scale);
let size_px = area.size_px();
(width, height) = (size_px.width as i32, size_px.height as i32);
}

let (hmenu, accels, has_menu) = match self.menu {
Some(menu) => {
let accels = menu.accels();
(menu.into_hmenu(), accels, true)
}
None => (0 as HMENU, None, false),
};

let window = WindowState {
hwnd: Cell::new(0 as HWND),
scale: Cell::new(scale),
Expand Down Expand Up @@ -1494,24 +1488,39 @@ impl WindowBuilder {
return Err(Error::NullHwnd);
}

if let Some(size_dp) = self.size {
if let Ok(scale) = handle.get_scale() {
let size_px = size_dp.to_px(scale);
if SetWindowPos(
hwnd,
HWND_TOPMOST,
0,
0,
size_px.width.round() as i32,
size_px.height.round() as i32,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER,
) == 0
// Now that the window has been created we finally have the correct scale,
// so we might need to update the window position & size.
if let Ok(new_scale) = handle.get_scale() {
if new_scale != scale {
scale = new_scale;
let mut flags =
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
let mut needs_any_change = false;

// Calculate the window position in pixels
if let Some(pos_dp) = self.position {
(pos_x, pos_y) = calculate_window_pos(parent_pos_dp, pos_dp, scale);
flags &= !SWP_NOMOVE;
needs_any_change = true;
}

// Calculate the window size in pixels
if let Some(size_dp) = self.size {
area = ScaledArea::from_dp(size_dp, scale);
let size_px = area.size_px();
(width, height) = (size_px.width as i32, size_px.height as i32);
flags &= !SWP_NOSIZE;
needs_any_change = true;
}

if needs_any_change
&& SetWindowPos(hwnd, HWND_TOPMOST, pos_x, pos_y, width, height, flags) == 0
{
warn!(
"failed to resize window: {}",
"failed to update window size/pos: {}",
Error::Hr(HRESULT_FROM_WIN32(GetLastError()))
);
};
}
}
}

Expand All @@ -1535,21 +1544,16 @@ impl WindowBuilder {
Ok(handle)
}
}
}

/// When creating a sub-window, we need to scale its position with respect to its parent.
/// If there is any error while scaling, log it as a warn and show sub-window in top left corner of screen/window.
fn scale_sub_window_position(
un_scaled_sub_window_position: Point,
parent_window_scale: Result<Scale, crate::Error>,
) -> Point {
match parent_window_scale {
Ok(s) => un_scaled_sub_window_position.to_px(s),
Err(e) => {
warn!("Error with scale: {:?}", e);
Point::new(0., 0.)
}
}
}
/// Returns a pair of integers representing the pixel values of the position.
fn calculate_window_pos(parent_pos_dp: Option<Point>, pos_dp: Point, scale: Scale) -> (i32, i32) {
let pos_px = if let Some(parent_pos_dp) = parent_pos_dp {
(parent_pos_dp + pos_dp.to_vec2()).to_px(scale)
} else {
pos_dp.to_px(scale)
};
(pos_px.x.round() as i32, pos_px.y.round() as i32)
}

/// Attempt to read the registry and see if the system is set to a dark or
Expand Down