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

Fix HiDPI issues related to image loading #235

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
57 changes: 30 additions & 27 deletions native-windows-gui/src/layouts/flexbox_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl FlexboxLayout {

/**
Returns the style of the parent control
Panic:
- The layout must have been successfully built otherwise this function will panic.
*/
Expand Down Expand Up @@ -100,8 +100,8 @@ impl FlexboxLayout {
}

/**
Add a new children in the layout with the stretch style.
Add a new children in the layout with the stretch style.
Panic:
* If the control is not a window-like control
* If the layout was not initialized
Expand All @@ -112,12 +112,12 @@ impl FlexboxLayout {
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}

let item = FlexboxLayoutItem {
control: c.into().hwnd().expect("Control must be window like (HWND handle)"),
style
};

inner.children.push(FlexboxLayoutChild::Item(item));
}

Expand All @@ -126,7 +126,7 @@ impl FlexboxLayout {

/**
Remove a children from the layout
Panic:
* If the control is not a window-like control
* If the control is not in the layout (see `has_child`)
Expand Down Expand Up @@ -178,7 +178,7 @@ impl FlexboxLayout {
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}

FlexboxLayoutChildren {
inner
}
Expand All @@ -204,9 +204,9 @@ impl FlexboxLayout {
}
}

/**
/**
Resize the layout to fit the parent window size
Panic:
- The layout must have been successfully built otherwise this function will panic.
*/
Expand All @@ -218,7 +218,7 @@ impl FlexboxLayout {

if let Some(parent_layout) = &inner.parent_layout {
parent_layout.fit()
}
}
else {
let (w, h) = unsafe { wh::get_window_size(inner.base) };
self.update_layout(w, h, (0, 0))
Expand Down Expand Up @@ -258,17 +258,20 @@ impl FlexboxLayout {
let Point { x, y } = layout.location;
let Size { width, height } = layout.size;

let (x, y) = unsafe { crate::win32::high_dpi::logical_to_physical_float(x, y) };
let (width, height) = unsafe { crate::win32::high_dpi::logical_to_physical_float(width, height) };

match child {
Child::Item(child) => {
positioner.defer_pos(child.control, last_handle.unwrap_or(std::ptr::null_mut()), x as i32 + offset.0, y as i32 + offset.1, width as i32, height as i32).ok();
last_handle.replace(child.control);
last_handle.replace(child.control);
},
Child::Flexbox(child) => {
let children_nodes = stretch.children(node)?;
FlexboxLayout::apply_layout_deferred(positioner, stretch, children_nodes, child.children().children(), last_handle, (x as i32, y as i32))?;
}
}

}

Ok(())
Expand All @@ -289,14 +292,14 @@ impl FlexboxLayout {
wh::set_window_position(child.control, x as i32 + offset.0, y as i32 + offset.1);
wh::set_window_size(child.control, width as u32, height as u32, false);
wh::set_window_after(child.control, *last_handle);
last_handle.replace(child.control);
last_handle.replace(child.control);
},
Child::Flexbox(child) => {
let children_nodes = stretch.children(node)?;
FlexboxLayout::apply_layout_immediate(stretch, children_nodes, child.children().children(), last_handle, (x as i32, y as i32))?;
}
}

}

Ok(())
Expand All @@ -321,7 +324,7 @@ impl FlexboxLayout {
if let Ok(mut positioner) = wh::DeferredWindowPositioner::new(item_count as i32) {
let layout_result = FlexboxLayout::apply_layout_deferred(&mut positioner, &mut stretch, nodes, self.children().children(), &mut None, offset);
positioner.end();

layout_result
}
else {
Expand Down Expand Up @@ -349,7 +352,7 @@ impl FlexboxLayoutBuilder {
/// Panics if `child` is not a window-like control.
pub fn child<W: Into<ControlHandle>>(mut self, child: W) -> FlexboxLayoutBuilder {
self.current_index = Some(self.layout.children.len());

let item = FlexboxLayoutItem {
control: child.into().hwnd().unwrap(),
style: Style::default()
Expand All @@ -363,7 +366,7 @@ impl FlexboxLayoutBuilder {
/// Add a new child layout to the layout build.
pub fn child_layout(mut self, child: &FlexboxLayout) -> FlexboxLayoutBuilder {
self.current_index = Some(self.layout.children.len());

self.layout.children.push(FlexboxLayoutChild::Flexbox(child.clone()));

self
Expand Down Expand Up @@ -521,7 +524,7 @@ impl FlexboxLayoutBuilder {

/**
Directly set the style parameter of the current child. Panics if `child` was not called before.
If defining style is too verbose, other method such as `size` can be used.
*/
pub fn style(mut self, style: Style) -> FlexboxLayoutBuilder {
Expand Down Expand Up @@ -583,16 +586,16 @@ impl FlexboxLayoutBuilder {
if layout_inner.handler.is_some() {
drop(unbind_raw_event_handler(layout_inner.handler.as_ref().unwrap()));
}
*layout_inner = self.layout;

*layout_inner = self.layout;
}

// Sets the parent_layout of any child layout to this layout
for child in layout.inner.borrow_mut().children.iter_mut() {
match child {
FlexboxLayoutChild::Item(_) => {},
FlexboxLayoutChild::Flexbox(child_layout) => {
child_layout.inner.borrow_mut().parent_layout.replace(layout.clone());
FlexboxLayoutChild::Flexbox(child_layout) => {
child_layout.inner.borrow_mut().parent_layout.replace(layout.clone());
},
}
}
Expand All @@ -602,9 +605,9 @@ impl FlexboxLayoutBuilder {

// Fetch a new ID for the layout handler
use std::sync::atomic::{AtomicUsize, Ordering};
static FLEX_LAYOUT_ID: AtomicUsize = AtomicUsize::new(0x9FFF);
static FLEX_LAYOUT_ID: AtomicUsize = AtomicUsize::new(0x9FFF);
let handler_id = FLEX_LAYOUT_ID.fetch_add(1, Ordering::SeqCst);

// Bind the event handler
let event_layout = layout.clone();
let cb = move |_h, msg, _w, l| {
Expand Down Expand Up @@ -636,7 +639,7 @@ impl FlexboxLayoutBuilder {
if self.auto_size {
let children_count = self.layout.children.len();
let size = 1.0f32 / (children_count as f32);
for child in self.layout.children.iter_mut() {
for child in self.layout.children.iter_mut() {
let child_size = match &self.layout.style.flex_direction {
FlexDirection::Row | FlexDirection::RowReverse => {
Size { width: Dimension::Percent(size), height: Dimension::Auto }
Expand Down Expand Up @@ -666,8 +669,8 @@ impl FlexboxLayoutBuilder {
if layout_inner.handler.is_some() {
drop(unbind_raw_event_handler(layout_inner.handler.as_ref().unwrap()));
}
*layout_inner = self.layout;

*layout_inner = self.layout;
}

Ok(())
Expand Down
19 changes: 11 additions & 8 deletions native-windows-gui/src/resources/image_list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::ptr;

use winapi::um::commctrl::{HIMAGELIST, ImageList_AddMasked};
use winapi::shared::windef::{HICON, HBITMAP};

use crate::{Bitmap, Icon, NwgError};
use std::ptr;
use crate::win32::high_dpi;


const NOT_BOUND: &'static str = "ImageList is not yet bound to a winapi object";
Expand All @@ -12,7 +15,7 @@ Image lists are used in controls such as tabs container and tree view in order t

There are two kinds of image list in Winapi: masked. This is a wrapper over the masked type.

Image list and the method that use them in controls are behind the "image-list" feature.
Image list and the method that use them in controls are behind the "image-list" feature.

**Builder parameters:**
* `size`: The size size of the images in the image list. Default `(32, 32)`
Expand Down Expand Up @@ -55,7 +58,7 @@ impl ImageList {
let mut size = (0, 0);
unsafe { ImageList_GetIconSize(self.handle, &mut size.0, &mut size.1); }

size
unsafe { high_dpi::physical_to_logical(size.0, size.1) }
}

/// Sets the size of the image list. This clears all current image data.
Expand All @@ -64,7 +67,7 @@ impl ImageList {

if self.handle.is_null() { panic!("{}", NOT_BOUND); }

let (w, h) = size;
let (w, h) = unsafe { high_dpi::logical_to_physical(size.0, size.1) };
unsafe { ImageList_SetIconSize(self.handle, w, h); }
}

Expand Down Expand Up @@ -116,7 +119,7 @@ impl ImageList {
unsafe {
let mut info: ICONINFO = ::std::mem::zeroed();
GetIconInfo(icon.handle as _, &mut info);

let i = ImageList_AddMasked(self.handle, info.hbmColor, 0);

DeleteObject(info.hbmMask as _);
Expand Down Expand Up @@ -165,7 +168,7 @@ impl ImageList {

if self.handle.is_null() { panic!("{}", NOT_BOUND); }
if bitmap.handle.is_null() { panic!("Bitmap was not initialized"); }

unsafe { ImageList_Replace(self.handle, index, bitmap.handle as HBITMAP, ptr::null_mut()); }
}

Expand Down Expand Up @@ -236,7 +239,7 @@ impl ImageListBuilder {
use winapi::um::commctrl::{ImageList_Create, ILC_COLOR32, ILC_MASK};

unsafe {
let (w, h) = self.size;
let (w, h) = high_dpi::logical_to_physical(self.size.0, self.size.1);
let handle = ImageList_Create(w, h, ILC_COLOR32 | ILC_MASK, self.initial, self.grow);
if handle.is_null() {
return Err(NwgError::resource_create("Failed to create image list"));
Expand All @@ -245,7 +248,7 @@ impl ImageListBuilder {
list.handle = handle;
list.owned = true;
}

Ok(())
}

Expand Down
42 changes: 38 additions & 4 deletions native-windows-gui/src/win32/high_dpi.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "high-dpi")]
use winapi::um::winuser::USER_DEFAULT_SCREEN_DPI;

#[cfg(not(feature = "high-dpi"))]
#[deprecated(note = "Specifying the default process DPI awareness via API is not recommended. Use the '<dpiAware>true</dpiAware>' setting in the application manifest. https://docs.microsoft.com/ru-ru/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process")]
pub unsafe fn set_dpi_awareness() {
Expand All @@ -17,7 +20,6 @@ pub fn scale_factor() -> f64 {

#[cfg(feature = "high-dpi")]
pub fn scale_factor() -> f64 {
use winapi::um::winuser::USER_DEFAULT_SCREEN_DPI;
let dpi = unsafe { dpi() };
f64::from(dpi) / f64::from(USER_DEFAULT_SCREEN_DPI)
}
Expand All @@ -30,13 +32,26 @@ pub unsafe fn logical_to_physical(x: i32, y: i32) -> (i32, i32) {
#[cfg(feature = "high-dpi")]
pub unsafe fn logical_to_physical(x: i32, y: i32) -> (i32, i32) {
use muldiv::MulDiv;
use winapi::um::winuser::USER_DEFAULT_SCREEN_DPI;
let dpi = dpi();
let x = x.mul_div_round(dpi, USER_DEFAULT_SCREEN_DPI).unwrap_or(x);
let y = y.mul_div_round(dpi, USER_DEFAULT_SCREEN_DPI).unwrap_or(y);
(x, y)
}

#[cfg(not(feature = "high-dpi"))]
pub unsafe fn logical_to_physical_float(x: f32, y: f32) -> (f32, f32) {
(x, y)
}

#[cfg(feature = "high-dpi")]
pub unsafe fn logical_to_physical_float(x: f32, y: f32) -> (f32, f32) {
let default_dpi = USER_DEFAULT_SCREEN_DPI as f32;
let dpi = dpi() as f32;
let x = x * dpi / default_dpi;
let y = y * dpi / default_dpi;
(x, y)
}

#[cfg(not(feature = "high-dpi"))]
pub unsafe fn physical_to_logical(x: i32, y: i32) -> (i32, i32) {
(x, y)
Expand All @@ -45,18 +60,37 @@ pub unsafe fn physical_to_logical(x: i32, y: i32) -> (i32, i32) {
#[cfg(feature = "high-dpi")]
pub unsafe fn physical_to_logical(x: i32, y: i32) -> (i32, i32) {
use muldiv::MulDiv;
use winapi::um::winuser::USER_DEFAULT_SCREEN_DPI;
let dpi = dpi();
let x = x.mul_div_round(USER_DEFAULT_SCREEN_DPI, dpi).unwrap_or(x);
let y = y.mul_div_round(USER_DEFAULT_SCREEN_DPI, dpi).unwrap_or(y);
(x, y)
}

#[allow(dead_code)]
#[cfg(feature = "high-dpi")]
pub unsafe fn physical_to_logical_float(x: f32, y: f32) -> (f32, f32) {
let default_dpi = USER_DEFAULT_SCREEN_DPI as f32;
let dpi = dpi() as f32;
let x = x * default_dpi / dpi;
let y = y * default_dpi / dpi;
(x, y)
}

#[allow(dead_code)]
#[cfg(not(feature = "high-dpi"))]
pub unsafe fn physical_to_logical_float(x: f32, y: f32) -> (f32, f32) {
(x, y)
}

pub unsafe fn dpi() -> i32 {
use winapi::um::winuser::GetDC;
use winapi::um::wingdi::GetDeviceCaps;
use winapi::um::wingdi::LOGPIXELSX;
let screen = GetDC(std::ptr::null_mut());
let dpi = GetDeviceCaps(screen, LOGPIXELSX);
dpi
if dpi == 0 {
USER_DEFAULT_SCREEN_DPI
} else {
dpi
}
}
Loading