Skip to content

Commit 4687942

Browse files
authored
Windows: add IconExtWindows::from_resource_name (#4137)
1 parent 0c89ea7 commit 4687942

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

src/changelog/unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ changelog entry.
7171
- Added `Window::safe_area`, which describes the area of the surface that is unobstructed.
7272
- On X11, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes.
7373
- Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`.
74+
- On Windows, add `IconExtWindows::from_resource_name`.
7475

7576
### Changed
7677

src/platform/windows.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,17 @@ impl DeviceIdExtWindows for DeviceId {
660660
}
661661

662662
/// Additional methods on `Icon` that are specific to Windows.
663+
///
664+
/// Windows icons can be created from files, or from the [`embedded resources`](https://learn.microsoft.com/en-us/windows/win32/menurc/about-resource-files).
665+
///
666+
/// The `ICON` resource definition statement use the following syntax:
667+
/// ```rc
668+
/// nameID ICON filename
669+
/// ```
670+
/// `nameID` is a unique name or a 16-bit unsigned integer value identifying the resource,
671+
/// `filename` is the name of the file that contains the resource.
672+
///
673+
/// More information about the `ICON` resource can be found at [`Microsoft Learn`](https://learn.microsoft.com/en-us/windows/win32/menurc/icon-resource) portal.
663674
pub trait IconExtWindows: Sized {
664675
/// Create an icon from a file path.
665676
///
@@ -671,14 +682,68 @@ pub trait IconExtWindows: Sized {
671682
fn from_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>)
672683
-> Result<Self, BadIcon>;
673684

674-
/// Create an icon from a resource embedded in this executable or library.
685+
/// Create an icon from a resource embedded in this executable or library by its ordinal id.
686+
///
687+
/// The valid `ordinal` values range from 1 to [`u16::MAX`] (inclusive). The value `0` is an
688+
/// invalid ordinal id, but it can be used with [`from_resource_name`] as `"0"`.
689+
///
690+
/// [`from_resource_name`]: IconExtWindows::from_resource_name
675691
///
676692
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
677693
/// icon size from the file.
678694
///
679695
/// In cases where the specified size does not exist in the file, Windows may perform scaling
680696
/// to get an icon of the desired size.
681697
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
698+
699+
/// Create an icon from a resource embedded in this executable or library by its name.
700+
///
701+
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
702+
/// icon size from the file.
703+
///
704+
/// In cases where the specified size does not exist in the file, Windows may perform scaling
705+
/// to get an icon of the desired size.
706+
///
707+
/// # Notes
708+
///
709+
/// Consider the following resource definition statements:
710+
/// ```rc
711+
/// app ICON "app.ico"
712+
/// 1 ICON "a.ico"
713+
/// 0027 ICON "custom.ico"
714+
/// 0 ICON "alt.ico"
715+
/// ```
716+
///
717+
/// Due to some internal implementation details of the resource embedding/loading process on
718+
/// Windows platform, strings that can be interpreted as 16-bit unsigned integers (`"1"`,
719+
/// `"002"`, etc.) cannot be used as valid resource names, and instead should be passed into
720+
/// [`from_resource`]:
721+
///
722+
/// [`from_resource`]: IconExtWindows::from_resource
723+
///
724+
/// ```rust,no_run
725+
/// use winit::platform::windows::IconExtWindows;
726+
/// use winit::window::Icon;
727+
///
728+
/// assert!(Icon::from_resource_name("app", None).is_ok());
729+
/// assert!(Icon::from_resource(1, None).is_ok());
730+
/// assert!(Icon::from_resource(27, None).is_ok());
731+
/// assert!(Icon::from_resource_name("27", None).is_err());
732+
/// assert!(Icon::from_resource_name("0027", None).is_err());
733+
/// ```
734+
///
735+
/// While `0` cannot be used as an ordinal id (see [`from_resource`]), it can be used as a
736+
/// name:
737+
///
738+
/// [`from_resource`]: IconExtWindows::from_resource
739+
///
740+
/// ```rust,no_run
741+
/// # use winit::platform::windows::IconExtWindows;
742+
/// # use winit::window::Icon;
743+
/// assert!(Icon::from_resource_name("0", None).is_ok());
744+
/// assert!(Icon::from_resource(0, None).is_err());
745+
/// ```
746+
fn from_resource_name(name: &str, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
682747
}
683748

684749
impl IconExtWindows for Icon {
@@ -694,4 +759,9 @@ impl IconExtWindows for Icon {
694759
let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?;
695760
Ok(Icon { inner: win_icon })
696761
}
762+
763+
fn from_resource_name(name: &str, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
764+
let win_icon = crate::platform_impl::WinIcon::from_resource_name(name, size)?;
765+
Ok(Icon { inner: win_icon })
766+
}
697767
}

src/platform_impl/windows/icon.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,28 @@ impl WinIcon {
112112
pub fn from_resource(
113113
resource_id: u16,
114114
size: Option<PhysicalSize<u32>>,
115+
) -> Result<Self, BadIcon> {
116+
Self::from_resource_ptr(resource_id as PCWSTR, size)
117+
}
118+
119+
pub fn from_resource_name(
120+
resource_name: &str,
121+
size: Option<PhysicalSize<u32>>,
122+
) -> Result<Self, BadIcon> {
123+
let wide_name = util::encode_wide(resource_name);
124+
Self::from_resource_ptr(wide_name.as_ptr(), size)
125+
}
126+
127+
fn from_resource_ptr(
128+
resource: PCWSTR,
129+
size: Option<PhysicalSize<u32>>,
115130
) -> Result<Self, BadIcon> {
116131
// width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size
117132
let (width, height) = size.map(Into::into).unwrap_or((0, 0));
118133
let handle = unsafe {
119134
LoadImageW(
120135
util::get_instance_handle(),
121-
resource_id as PCWSTR,
136+
resource,
122137
IMAGE_ICON,
123138
width,
124139
height,

0 commit comments

Comments
 (0)