Skip to content

Commit

Permalink
Respect scale when setting window position during creation. (#2296)
Browse files Browse the repository at this point in the history
  • Loading branch information
xStrom committed Dec 1, 2022
1 parent 52d5529 commit ef936e0
Showing 1 changed file with 70 additions and 66 deletions.
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 @@ -1361,32 +1361,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 @@ -1399,29 +1381,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 @@ -1496,24 +1490,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 @@ -1537,21 +1546,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

0 comments on commit ef936e0

Please sign in to comment.