diff --git a/native-windows-gui/src/layouts/flexbox_layout.rs b/native-windows-gui/src/layouts/flexbox_layout.rs index a489e59b..ec9c99e7 100644 --- a/native-windows-gui/src/layouts/flexbox_layout.rs +++ b/native-windows-gui/src/layouts/flexbox_layout.rs @@ -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. */ @@ -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 @@ -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)); } @@ -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`) @@ -178,7 +178,7 @@ impl FlexboxLayout { if inner.base.is_null() { panic!("Flexbox layout is not yet initialized!"); } - + FlexboxLayoutChildren { inner } @@ -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. */ @@ -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)) @@ -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(()) @@ -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(()) @@ -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 { @@ -349,7 +352,7 @@ impl FlexboxLayoutBuilder { /// Panics if `child` is not a window-like control. pub fn child>(mut self, child: W) -> FlexboxLayoutBuilder { self.current_index = Some(self.layout.children.len()); - + let item = FlexboxLayoutItem { control: child.into().hwnd().unwrap(), style: Style::default() @@ -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 @@ -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 { @@ -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()); }, } } @@ -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| { @@ -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 } @@ -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(()) diff --git a/native-windows-gui/src/resources/image_list.rs b/native-windows-gui/src/resources/image_list.rs index 31038608..4f8a4063 100644 --- a/native-windows-gui/src/resources/image_list.rs +++ b/native-windows-gui/src/resources/image_list.rs @@ -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"; @@ -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)` @@ -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. @@ -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); } } @@ -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 _); @@ -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()); } } @@ -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")); @@ -245,7 +248,7 @@ impl ImageListBuilder { list.handle = handle; list.owned = true; } - + Ok(()) } diff --git a/native-windows-gui/src/win32/high_dpi.rs b/native-windows-gui/src/win32/high_dpi.rs index 3d23bd07..a64e9860 100644 --- a/native-windows-gui/src/win32/high_dpi.rs +++ b/native-windows-gui/src/win32/high_dpi.rs @@ -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 'true' 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() { @@ -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) } @@ -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) @@ -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 + } } diff --git a/native-windows-gui/src/win32/resources_helper.rs b/native-windows-gui/src/win32/resources_helper.rs index 027ef802..cd834afd 100644 --- a/native-windows-gui/src/win32/resources_helper.rs +++ b/native-windows-gui/src/win32/resources_helper.rs @@ -3,6 +3,7 @@ use winapi::ctypes::c_int; use winapi::um::winnt::HANDLE; use crate::resources::OemImage; +use crate::win32::high_dpi; use super::base_helper::{get_system_error, to_utf16}; #[allow(unused_imports)] use std::{ptr, mem}; @@ -25,23 +26,23 @@ pub fn is_bitmap(handle: HBITMAP) -> bool { pub fn destroy_icon(icon: HANDLE) { unsafe { winapi::um::winuser::DestroyIcon(icon as _); } -} +} pub fn destroy_cursor(cursor: HANDLE) { unsafe { winapi::um::winuser::DestroyCursor(cursor as _); } -} +} pub fn destroy_obj(obj: HANDLE) { unsafe { winapi::um::wingdi::DeleteObject(obj as _); } -} +} pub unsafe fn build_font( size: i32, weight: u32, style: [bool; 3], family_name: Option<&str>, -) -> Result -{ +) -> Result +{ use winapi::um::wingdi::{DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH}; use winapi::um::wingdi::CreateFontW; let [use_italic, use_underline, use_strikeout] = style; @@ -56,7 +57,7 @@ pub unsafe fn build_font( family_name_ptr = ptr::null(); } - let (size, _) = super::high_dpi::logical_to_physical(size as i32, 0); + let (size, _) = high_dpi::logical_to_physical(size as i32, 0); let handle = CreateFontW( size as c_int, // nHeight @@ -95,8 +96,9 @@ pub unsafe fn build_image<'a>( let filepath = to_utf16(source); let (width, height) = size.unwrap_or((0,0)); + let (width, height) = high_dpi::logical_to_physical(width as i32, height as i32); - let mut handle = LoadImageW(ptr::null_mut(), filepath.as_ptr(), image_type, width as i32, height as i32, LR_LOADFROMFILE); + let mut handle = LoadImageW(ptr::null_mut(), filepath.as_ptr(), image_type, width, height, LR_LOADFROMFILE); if handle.is_null() { let (code, _) = get_system_error(); if code == 2 && !strict { @@ -116,7 +118,7 @@ pub unsafe fn build_image<'a>( }, _ => { unreachable!() } }; - + } } @@ -138,15 +140,16 @@ pub unsafe fn build_image_decoder<'a>( use crate::ImageDecoder; let decoder = ImageDecoder::new()?; - + let mut image_frame = decoder .from_filename(source)? .frame(0)?; if let Some((width, height)) = size { - image_frame = decoder.resize_image(&image_frame, [width, height])?; + let (width, height) = high_dpi::logical_to_physical(width as i32, height as i32); + image_frame = decoder.resize_image(&image_frame, [width as u32, height as u32])?; } - + let mut bitmap = image_frame.as_bitmap()?; bitmap.owned = false; @@ -163,15 +166,16 @@ pub unsafe fn build_image_decoder_from_memory<'a>( use crate::ImageDecoder; let decoder = ImageDecoder::new()?; - + let mut image_frame = decoder .from_stream(src)? .frame(0)?; if let Some((width, height)) = size { - image_frame = decoder.resize_image(&image_frame, [width, height])?; + let (width, height) = high_dpi::logical_to_physical(width as i32, height as i32); + image_frame = decoder.resize_image(&image_frame, [width as u32, height as u32])?; } - + let mut bitmap = image_frame.as_bitmap()?; bitmap.owned = false; @@ -182,13 +186,14 @@ pub unsafe fn build_image_decoder_from_memory<'a>( pub unsafe fn build_oem_image( source: OemImage, size: Option<(u32, u32)>, -) -> Result +) -> Result { use winapi::um::winuser::{LR_DEFAULTSIZE, LR_SHARED, IMAGE_ICON, IMAGE_CURSOR, IMAGE_BITMAP}; use winapi::um::winuser::LoadImageW; use winapi::shared::ntdef::LPCWSTR; let (width, height) = size.unwrap_or((0,0)); + let (width, height) = high_dpi::logical_to_physical(width as i32, height as i32); let (c_res_type, res_identifier) = match source { OemImage::Bitmap(b) => { @@ -208,7 +213,7 @@ pub unsafe fn build_oem_image( LR_SHARED }; - let handle = LoadImageW(ptr::null_mut(), res_identifier, c_res_type, width as i32, height as i32, flags); + let handle = LoadImageW(ptr::null_mut(), res_identifier, c_res_type, width, height, flags); if handle.is_null() { Err( NwgError::resource_create("Failed to create image from system resource") ) @@ -218,7 +223,7 @@ pub unsafe fn build_oem_image( } -/** +/** Create a bitmap from memory. Only supports bitmap. Enable the `image-decoder` to load more image type from memory The memory must contain the whole file (including the bitmap header). */ @@ -256,7 +261,7 @@ pub unsafe fn bitmap_from_memory(source: &[u8]) -> Result { let header = BITMAPINFOHEADER { biSize: mem::size_of::() as DWORD, - biWidth: w as LONG, biHeight: h as LONG, + biWidth: w as LONG, biHeight: h as LONG, biPlanes: 1, biBitCount: 24, biCompression: BI_RGB, biSizeImage: (w * h * 3) as u32, biXPelsPerMeter: 0, biYPelsPerMeter: 0, @@ -278,7 +283,7 @@ pub unsafe fn bitmap_from_memory(source: &[u8]) -> Result { return Ok(bitmap as HANDLE); } -/** +/** Create a bitmap from memory. The source can be any image type supported by the windows imaging component. The memory must contain the whole file (including the file header). */ @@ -343,7 +348,7 @@ pub unsafe fn create_file_dialog<'a, 'b>( multiselect: bool, default_folder: Option, filters: Option -) -> Result<*mut IFileDialog, NwgError> +) -> Result<*mut IFileDialog, NwgError> { use winapi::um::shobjidl_core::{CLSID_FileSaveDialog, CLSID_FileOpenDialog}; use winapi::um::shobjidl::{FOS_PICKFOLDERS, FOS_ALLOWMULTISELECT, FOS_FORCEFILESYSTEM}; @@ -366,10 +371,10 @@ pub unsafe fn create_file_dialog<'a, 'b>( // Set dialog options if file_dialog.GetOptions(&mut flags) != S_OK { - file_dialog.Release(); + file_dialog.Release(); return Err(NwgError::file_dialog("Filedialog creation failed")); } - + let use_dir = if action == FileDialogAction::OpenDirectory { FOS_PICKFOLDERS } else { 0 }; let multiselect = if multiselect { FOS_ALLOWMULTISELECT } else { 0 }; if file_dialog.SetOptions(flags | FOS_FORCEFILESYSTEM | use_dir | multiselect) != S_OK { @@ -377,7 +382,7 @@ pub unsafe fn create_file_dialog<'a, 'b>( return Err(NwgError::file_dialog("Filedialog creation failed")); } - + // Set the default folder match &default_folder { &Some(ref f) => match file_dialog_set_default_folder(file_dialog, f) { @@ -424,7 +429,7 @@ pub unsafe fn file_dialog_set_default_folder<'a>(dialog: &mut IFileDialog, folde let shellitem = &mut *shellitem; let mut file_properties: SFGAOF = 0; - + let results = shellitem.GetAttributes(SFGAO_FOLDER, &mut file_properties); if results != S_OK && results != S_FALSE { @@ -466,7 +471,7 @@ pub unsafe fn file_dialog_set_filters<'a>(dialog: &mut IFileDialog, filters: &'a let (_name, _filter) = f.split_at(end.unwrap()); let (name, filter) = (to_utf16(_name), to_utf16(&_filter[1.._filter.len()-1])); - + raw_filters.push(COMDLG_FILTERSPEC{ pszName: name.as_ptr(), pszSpec: filter.as_ptr() }); keep_alive.push( (name, filter) ); } @@ -483,7 +488,7 @@ pub unsafe fn file_dialog_set_filters<'a>(dialog: &mut IFileDialog, filters: &'a #[cfg(feature = "file-dialog")] pub unsafe fn filedialog_get_item(dialog: &mut IFileDialog) -> Result { use winapi::shared::winerror::S_OK; - + let mut _item: *mut IShellItem = ptr::null_mut(); if dialog.GetResult(&mut _item) != S_OK { @@ -500,7 +505,7 @@ pub unsafe fn filedialog_get_item(dialog: &mut IFileDialog) -> Result Result, NwgError> { use winapi::um::shobjidl::IShellItemArray; use winapi::shared::{winerror::S_OK, minwindef::DWORD}; - + let mut _item: *mut IShellItem = ptr::null_mut(); let mut _items: *mut IShellItemArray = ptr::null_mut(); @@ -511,7 +516,7 @@ pub unsafe fn filedialog_get_items(dialog: &mut IFileOpenDialog) -> Result = Vec::with_capacity(count as usize); for i in 0..count { items.GetItemAt(i, &mut _item); @@ -560,7 +565,7 @@ pub unsafe fn file_dialog_options(dialog: &mut IFileDialog) -> Result Result<(), NwgError> { use winapi::shared::winerror::S_OK; - + let mut flags = file_dialog_options(dialog)?; flags = match enabled { true => flags | flag,