diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 1144622c14..e8344b53ca 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -71,6 +71,7 @@ changelog entry. - Added `Window::safe_area`, which describes the area of the surface that is unobstructed. - On X11, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes. - Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`. +- On Windows, add `IconExtWindows::from_resource_name`. ### Changed diff --git a/src/platform/windows.rs b/src/platform/windows.rs index d93a05c21e..05172b7745 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -660,6 +660,17 @@ impl DeviceIdExtWindows for DeviceId { } /// Additional methods on `Icon` that are specific to Windows. +/// +/// Windows icons can be created from files, or from the [`embedded resources`](https://learn.microsoft.com/en-us/windows/win32/menurc/about-resource-files). +/// +/// The `ICON` resource definition statement use the following syntax: +/// ```rc +/// nameID ICON filename +/// ``` +/// `nameID` is a unique name or a 16-bit unsigned integer value identifying the resource, +/// `filename` is the name of the file that contains the resource. +/// +/// More information about the `ICON` resource can be found at [`Microsoft Learn`](https://learn.microsoft.com/en-us/windows/win32/menurc/icon-resource) portal. pub trait IconExtWindows: Sized { /// Create an icon from a file path. /// @@ -671,7 +682,12 @@ pub trait IconExtWindows: Sized { fn from_path>(path: P, size: Option>) -> Result; - /// Create an icon from a resource embedded in this executable or library. + /// Create an icon from a resource embedded in this executable or library by its ordinal id. + /// + /// The valid `ordinal` values range from 1 to [`u16::MAX`] (inclusive). The value `0` is an + /// invalid ordinal id, but it can be used with [`from_resource_name`] as `"0"`. + /// + /// [`from_resource_name`]: IconExtWindows::from_resource_name /// /// Specify `size` to load a specific icon size from the file, or `None` to load the default /// icon size from the file. @@ -679,6 +695,55 @@ pub trait IconExtWindows: Sized { /// In cases where the specified size does not exist in the file, Windows may perform scaling /// to get an icon of the desired size. fn from_resource(ordinal: u16, size: Option>) -> Result; + + /// Create an icon from a resource embedded in this executable or library by its name. + /// + /// Specify `size` to load a specific icon size from the file, or `None` to load the default + /// icon size from the file. + /// + /// In cases where the specified size does not exist in the file, Windows may perform scaling + /// to get an icon of the desired size. + /// + /// # Notes + /// + /// Consider the following resource definition statements: + /// ```rc + /// app ICON "app.ico" + /// 1 ICON "a.ico" + /// 0027 ICON "custom.ico" + /// 0 ICON "alt.ico" + /// ``` + /// + /// Due to some internal implementation details of the resource embedding/loading process on + /// Windows platform, strings that can be interpreted as 16-bit unsigned integers (`"1"`, + /// `"002"`, etc.) cannot be used as valid resource names, and instead should be passed into + /// [`from_resource`]: + /// + /// [`from_resource`]: IconExtWindows::from_resource + /// + /// ```rust,no_run + /// use winit::platform::windows::IconExtWindows; + /// use winit::window::Icon; + /// + /// assert!(Icon::from_resource_name("app", None).is_ok()); + /// assert!(Icon::from_resource(1, None).is_ok()); + /// assert!(Icon::from_resource(27, None).is_ok()); + /// assert!(Icon::from_resource_name("27", None).is_err()); + /// assert!(Icon::from_resource_name("0027", None).is_err()); + /// ``` + /// + /// While `0` cannot be used as an ordinal id (see [`from_resource`]), it can be used as a + /// name: + /// + /// [`from_resource`]: IconExtWindows::from_resource + /// + /// ```rust,no_run + /// # use winit::platform::windows::IconExtWindows; + /// # use winit::window::Icon; + /// assert!(Icon::from_resource_name("0", None).is_ok()); + /// assert!(Icon::from_resource(0, None).is_err()); + /// ``` + fn from_resource_name(name: &str, size: Option>) -> Result; } impl IconExtWindows for Icon { @@ -694,4 +759,9 @@ impl IconExtWindows for Icon { let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?; Ok(Icon { inner: win_icon }) } + + fn from_resource_name(name: &str, size: Option>) -> Result { + let win_icon = crate::platform_impl::WinIcon::from_resource_name(name, size)?; + Ok(Icon { inner: win_icon }) + } } diff --git a/src/platform_impl/windows/icon.rs b/src/platform_impl/windows/icon.rs index 7e5ed29ca7..f966a0f58b 100644 --- a/src/platform_impl/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -112,13 +112,28 @@ impl WinIcon { pub fn from_resource( resource_id: u16, size: Option>, + ) -> Result { + Self::from_resource_ptr(resource_id as PCWSTR, size) + } + + pub fn from_resource_name( + resource_name: &str, + size: Option>, + ) -> Result { + let wide_name = util::encode_wide(resource_name); + Self::from_resource_ptr(wide_name.as_ptr(), size) + } + + fn from_resource_ptr( + resource: PCWSTR, + size: Option>, ) -> Result { // width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size let (width, height) = size.map(Into::into).unwrap_or((0, 0)); let handle = unsafe { LoadImageW( util::get_instance_handle(), - resource_id as PCWSTR, + resource, IMAGE_ICON, width, height,