icon: refactor Icon to be dyn

Same as for `CustomCursor`. However, the API uses `dyn` stuff only
because of `Windows` backend at the time of writing, generally, platforms
should just have a separate method that deals with all of that, and e.g.
top-level winit only guarantees `Rgba`.
This commit is contained in:
Kirill Chibisov 2025-03-23 20:02:07 +03:00
parent cdbdd974fb
commit 07c25b9703
16 changed files with 230 additions and 211 deletions

View file

@ -20,6 +20,7 @@ use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::error::RequestError;
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::icon::RgbaIcon;
use winit::keyboard::{Key, ModifiersState};
use winit::monitor::Fullscreen;
#[cfg(macos_platform)]
@ -1156,7 +1157,7 @@ fn load_icon(bytes: &[u8]) -> Icon {
let rgba = image.into_raw();
(rgba, width, height)
};
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
RgbaIcon::new(icon_rgba, icon_width, icon_height).expect("Failed to open icon").into()
}
fn modifiers_to_string(mods: ModifiersState) -> String {

View file

@ -76,6 +76,8 @@ changelog entry.
- On X11, set an "area" attribute on XIM input connection to convey the cursor area.
- Implement `CustomCursorProvider` for `CustomCursor` to access cursor API.
- Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`.
- Implement `CustomIconProvider` for `RgbaIcon`.
- Add `icon` module that exposes winit's icon API.
### Changed
@ -196,6 +198,7 @@ changelog entry.
- Move `window::Fullscreen` to `monitor::Fullscreen`.
- Renamed "super" key to "meta", to match the naming in the W3C specification.
`NamedKey::Super` still exists, but it's non-functional and deprecated, `NamedKey::Meta` should be used instead.
- Move `IconExtWindows` into `WinIcon`.
### Removed
@ -230,9 +233,10 @@ changelog entry.
- Remove `Window::inner_position`, use the new `Window::surface_position` instead.
- Remove `CustomCursorExtWeb`, use the `CustomCursorSource`.
- Remove `CustomCursor::from_rgba`, use `CustomCursorSource` instead.
- Removed `ApplicationHandler::exited`, the event loop being shut down can now be listened to in
- Remove `ApplicationHandler::exited`, the event loop being shut down can now be listened to in
the `Drop` impl on the application handler.
- Removed `NamedKey::Space`, match on `Key::Character(" ")` instead.
- Remove `NamedKey::Space`, match on `Key::Character(" ")` instead.
- Remove `PartialEq` impl for `WindowAttributes`.
### Fixed

View file

@ -1,7 +1,20 @@
use std::error::Error;
use std::sync::Arc;
use std::{fmt, io, mem};
use crate::platform_impl::PlatformIcon;
use crate::utils::{impl_dyn_casting, AsAny};
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
/// An icon used for the window titlebar, taskbar, etc.
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Icon(pub(crate) Arc<dyn IconProvider>);
// TODO remove that once split.
pub trait IconProvider: AsAny + fmt::Debug + Send + Sync {}
impl_dyn_casting!(IconProvider);
#[repr(C)]
#[derive(Debug)]
@ -12,10 +25,8 @@ pub(crate) struct Pixel {
pub(crate) a: u8,
}
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
#[derive(Debug)]
/// An error produced when using [`Icon::from_rgba`] with invalid arguments.
/// An error produced when using [`RgbaIcon::new`] with invalid arguments.
pub enum BadIcon {
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
/// safely interpreted as 32bpp RGBA pixels.
@ -50,69 +61,48 @@ impl fmt::Display for BadIcon {
impl Error for BadIcon {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct RgbaIcon {
pub(crate) rgba: Vec<u8>,
#[derive(Debug, Clone)]
pub struct RgbaIcon {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) rgba: Vec<u8>,
}
/// For platforms which don't have window icons (e.g. Web)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct NoIcon;
#[allow(dead_code)] // These are not used on every platform
mod constructors {
use super::*;
impl RgbaIcon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() });
}
let pixel_count = rgba.len() / PIXEL_SIZE;
if pixel_count != (width * height) as usize {
Err(BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height: (width * height) as usize,
pixel_count,
})
} else {
Ok(RgbaIcon { rgba, width, height })
}
impl RgbaIcon {
pub fn new(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() });
}
let pixel_count = rgba.len() / PIXEL_SIZE;
if pixel_count != (width * height) as usize {
Err(BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height: (width * height) as usize,
pixel_count,
})
} else {
Ok(RgbaIcon { rgba, width, height })
}
}
impl NoIcon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
// Create the rgba icon anyway to validate the input
let _ = RgbaIcon::from_rgba(rgba, width, height)?;
Ok(NoIcon)
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn buffer(&self) -> &[u8] {
self.rgba.as_slice()
}
}
/// An icon used for the window titlebar, taskbar, etc.
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Icon {
pub(crate) inner: PlatformIcon,
}
impl IconProvider for RgbaIcon {}
impl fmt::Debug for Icon {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.inner, formatter)
}
}
impl Icon {
/// Creates an icon from 32bpp RGBA data.
///
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let _span = tracing::debug_span!("winit::Icon::from_rgba", width, height).entered();
Ok(Icon { inner: PlatformIcon::from_rgba(rgba, width, height)? })
impl From<RgbaIcon> for Icon {
fn from(value: RgbaIcon) -> Self {
Self(Arc::new(value))
}
}

View file

@ -302,7 +302,7 @@ pub mod error;
mod cursor;
pub mod event;
pub mod event_loop;
mod icon;
pub mod icon;
pub mod keyboard;
pub mod monitor;
mod platform_impl;

View file

@ -6,6 +6,7 @@ use std::borrow::Borrow;
use std::ffi::c_void;
use std::ops::Deref;
use std::path::Path;
use std::sync::Arc;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -15,7 +16,9 @@ use windows_sys::Win32::Foundation::HANDLE;
use crate::dpi::PhysicalSize;
use crate::event::DeviceId;
use crate::event_loop::EventLoopBuilder;
use crate::window::{BadIcon, Icon, Window, WindowAttributes};
use crate::icon::BadIcon;
use crate::platform_impl::RaiiIcon;
use crate::window::{Icon, Window, WindowAttributes};
/// Window Handle type used by Win32 API
pub type HWND = *mut c_void;
@ -637,7 +640,7 @@ impl DeviceIdExtWindows for DeviceId {
}
}
/// Additional methods on `Icon` that are specific to Windows.
/// Windows specific `Icon`.
///
/// Windows icons can be created from files, or from the [`embedded resources`](https://learn.microsoft.com/en-us/windows/win32/menurc/about-resource-files).
///
@ -649,7 +652,12 @@ impl DeviceIdExtWindows for DeviceId {
/// `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 {
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct WinIcon {
pub(crate) inner: Arc<RaiiIcon>,
}
impl WinIcon {
/// Create an icon from a file path.
///
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
@ -657,22 +665,31 @@ 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_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>)
-> Result<Self, BadIcon>;
pub fn from_path<P: AsRef<Path>>(
path: P,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
Self::from_path_impl(path, size)
}
/// 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
/// [`from_resource_name`]: Self::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.
///
/// 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<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
pub fn from_resource(
resource_id: u16,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
Self::from_resource_impl(resource_id, size)
}
/// Create an icon from a resource embedded in this executable or library by its name.
///
@ -697,17 +714,16 @@ pub trait IconExtWindows: Sized {
/// `"002"`, etc.) cannot be used as valid resource names, and instead should be passed into
/// [`from_resource`]:
///
/// [`from_resource`]: IconExtWindows::from_resource
/// [`from_resource`]: Self::from_resource
///
/// ```rust,no_run
/// use winit::platform::windows::IconExtWindows;
/// use winit::window::Icon;
/// use winit::platform::windows::WinIcon;
///
/// 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());
/// assert!(WinIcon::from_resource_name("app", None).is_ok());
/// assert!(WinIcon::from_resource(1, None).is_ok());
/// assert!(WinIcon::from_resource(27, None).is_ok());
/// assert!(WinIcon::from_resource_name("27", None).is_err());
/// assert!(WinIcon::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
@ -716,30 +732,21 @@ pub trait IconExtWindows: Sized {
/// [`from_resource`]: IconExtWindows::from_resource
///
/// ```rust,no_run
/// # use winit::platform::windows::IconExtWindows;
/// # use winit::platform::windows::WinIcon;
/// # use winit::window::Icon;
/// assert!(Icon::from_resource_name("0", None).is_ok());
/// assert!(Icon::from_resource(0, None).is_err());
/// assert!(WinIcon::from_resource_name("0", None).is_ok());
/// assert!(WinIcon::from_resource(0, None).is_err());
/// ```
fn from_resource_name(name: &str, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
}
impl IconExtWindows for Icon {
fn from_path<P: AsRef<Path>>(
path: P,
pub fn from_resource_name(
resource_name: &str,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_path(path, size)?;
Ok(Icon { inner: win_icon })
}
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon })
}
fn from_resource_name(name: &str, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_resource_name(name, size)?;
Ok(Icon { inner: win_icon })
Self::from_resource_name_impl(resource_name, size)
}
}
impl From<WinIcon> for Icon {
fn from(value: WinIcon) -> Self {
Self(Arc::new(value))
}
}

View file

@ -20,7 +20,6 @@ use crate::event_loop::{
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use crate::platform::pump_events::PumpStatus;
use crate::window::{

View file

@ -21,4 +21,3 @@ pub(crate) use self::event_loop::{
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::Window;
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::icon::NoIcon as PlatformIcon;

View file

@ -14,7 +14,6 @@ pub(crate) use self::event_loop::{
};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Debug)]
pub enum OsError {}

View file

@ -13,7 +13,6 @@ use crate::application::ApplicationHandler;
use crate::dpi::Size;
use crate::error::{EventLoopError, NotSupportedError};
use crate::event_loop::ActiveEventLoop;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
use crate::platform::pump_events::PumpStatus;
#[cfg(x11_platform)]
use crate::platform::x11::WindowType as XWindowType;

View file

@ -28,15 +28,16 @@ use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}
use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent};
use crate::event_loop::AsyncRequestSerial;
use crate::icon::RgbaIcon;
use crate::monitor::{
Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider, VideoMode,
};
use crate::platform::x11::WindowType;
use crate::platform_impl::common;
use crate::platform_impl::x11::atoms::*;
use crate::platform_impl::x11::{
xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error,
};
use crate::platform_impl::{common, PlatformIcon};
use crate::window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
@ -203,7 +204,11 @@ impl CoreWindow for Window {
}
fn set_window_icon(&self, window_icon: Option<crate::window::Icon>) {
self.0.set_window_icon(window_icon.map(|inner| inner.inner))
let icon = match window_icon.as_ref() {
Some(icon) => icon.0.cast_ref::<RgbaIcon>(),
None => None,
};
self.0.set_window_icon(icon)
}
fn set_ime_cursor_area(&self, position: Position, size: Size) {
@ -765,8 +770,10 @@ impl UnownedWindow {
.check());
// Set window icons
if let Some(icon) = window_attrs.window_icon {
leap!(window.set_icon_inner(icon.inner)).ignore_error();
if let Some(icon) =
window_attrs.window_icon.as_ref().and_then(|icon| icon.0.cast_ref::<RgbaIcon>())
{
leap!(window.set_icon_inner(icon)).ignore_error();
}
// Opt into handling window close and resize synchronization
@ -1404,7 +1411,7 @@ impl UnownedWindow {
self.xconn.flush_requests().expect("Failed to set window-level state");
}
fn set_icon_inner(&self, icon: PlatformIcon) -> Result<VoidCookie<'_>, X11Error> {
fn set_icon_inner(&self, icon: &RgbaIcon) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let icon_atom = atoms[_NET_WM_ICON];
let data = icon.to_cardinals();
@ -1431,7 +1438,7 @@ impl UnownedWindow {
}
#[inline]
pub(crate) fn set_window_icon(&self, icon: Option<PlatformIcon>) {
pub(crate) fn set_window_icon(&self, icon: Option<&RgbaIcon>) {
match icon {
Some(icon) => self.set_icon_inner(icon),
None => self.unset_icon_inner(),

View file

@ -4,7 +4,6 @@ use std::{fmt, str};
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
pub use self::window::Window;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
mod event_loop;
mod window;

View file

@ -42,4 +42,3 @@ pub(crate) use self::monitor::{
};
use self::web_sys as backend;
pub use self::window::{PlatformSpecificWindowAttributes, Window};
pub(crate) use crate::icon::NoIcon as PlatformIcon;

View file

@ -5,13 +5,12 @@ use std::{fmt, io, mem, ptr};
use cursor_icon::CursorIcon;
use windows_sys::core::PCWSTR;
use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::Graphics::Gdi::{
CreateBitmap, CreateCompatibleBitmap, DeleteObject, GetDC, ReleaseDC, SetBitmapBits,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateIcon, CreateIconIndirect, DestroyCursor, DestroyIcon, LoadImageW, SendMessageW, HCURSOR,
HICON, ICONINFO, ICON_BIG, ICON_SMALL, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE, WM_SETICON,
CreateIcon, CreateIconIndirect, DestroyCursor, DestroyIcon, LoadImageW, HCURSOR, HICON,
ICONINFO, ICON_BIG, ICON_SMALL, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE,
};
use super::util;
@ -19,71 +18,12 @@ use crate::cursor::{CursorImage, CustomCursorProvider};
use crate::dpi::PhysicalSize;
use crate::error::RequestError;
use crate::icon::*;
impl Pixel {
fn convert_to_bgra(&mut self) {
mem::swap(&mut self.r, &mut self.b);
}
}
impl RgbaIcon {
fn into_windows_icon(self) -> Result<WinIcon, BadIcon> {
let rgba = self.rgba;
let pixel_count = rgba.len() / PIXEL_SIZE;
let mut and_mask = Vec::with_capacity(pixel_count);
let pixels =
unsafe { std::slice::from_raw_parts_mut(rgba.as_ptr() as *mut Pixel, pixel_count) };
for pixel in pixels {
and_mask.push(pixel.a.wrapping_sub(u8::MAX)); // invert alpha channel
pixel.convert_to_bgra();
}
assert_eq!(and_mask.len(), pixel_count);
let handle = unsafe {
CreateIcon(
ptr::null_mut(),
self.width as i32,
self.height as i32,
1,
(PIXEL_SIZE * 8) as u8,
and_mask.as_ptr(),
rgba.as_ptr(),
)
};
if !handle.is_null() {
Ok(WinIcon::from_handle(handle))
} else {
Err(BadIcon::OsError(io::Error::last_os_error()))
}
}
}
#[derive(Debug)]
pub enum IconType {
Small = ICON_SMALL as isize,
Big = ICON_BIG as isize,
}
#[derive(Debug, PartialEq, Eq, Hash)]
struct RaiiIcon {
handle: HICON,
}
unsafe impl Send for RaiiIcon {}
unsafe impl Sync for RaiiIcon {}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct WinIcon {
inner: Arc<RaiiIcon>,
}
use crate::platform::windows::WinIcon;
unsafe impl Send for WinIcon {}
impl WinIcon {
pub fn as_raw_handle(&self) -> HICON {
self.inner.handle
}
pub fn from_path<P: AsRef<Path>>(
pub(crate) fn from_path_impl<P: AsRef<Path>>(
path: P,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
@ -109,14 +49,14 @@ impl WinIcon {
}
}
pub fn from_resource(
pub(crate) fn from_resource_impl(
resource_id: u16,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
Self::from_resource_ptr(resource_id as PCWSTR, size)
}
pub fn from_resource_name(
pub(crate) fn from_resource_name_impl(
resource_name: &str,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
@ -147,14 +87,36 @@ impl WinIcon {
}
}
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let rgba_icon = RgbaIcon::from_rgba(rgba, width, height)?;
rgba_icon.into_windows_icon()
pub(crate) fn as_raw_handle(&self) -> HICON {
self.inner.handle
}
pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) {
unsafe {
SendMessageW(hwnd, WM_SETICON, icon_type as usize, self.as_raw_handle() as isize);
pub(crate) fn from_rgba(rgba: &RgbaIcon) -> Result<Self, BadIcon> {
let pixel_count = rgba.rgba.len() / PIXEL_SIZE;
let mut and_mask = Vec::with_capacity(pixel_count);
let pixels = unsafe {
std::slice::from_raw_parts_mut(rgba.rgba.as_ptr() as *mut Pixel, pixel_count)
};
for pixel in pixels {
and_mask.push(pixel.a.wrapping_sub(u8::MAX)); // invert alpha channel
pixel.convert_to_bgra();
}
assert_eq!(and_mask.len(), pixel_count);
let handle = unsafe {
CreateIcon(
ptr::null_mut(),
rgba.width as i32,
rgba.height as i32,
1,
(PIXEL_SIZE * 8) as u8,
and_mask.as_ptr(),
rgba.rgba.as_ptr(),
)
};
if !handle.is_null() {
Ok(WinIcon::from_handle(handle))
} else {
Err(BadIcon::OsError(io::Error::last_os_error()))
}
}
@ -163,11 +125,7 @@ impl WinIcon {
}
}
impl Drop for RaiiIcon {
fn drop(&mut self) {
unsafe { DestroyIcon(self.handle) };
}
}
impl IconProvider for WinIcon {}
impl fmt::Debug for WinIcon {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
@ -175,9 +133,29 @@ impl fmt::Debug for WinIcon {
}
}
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
unsafe {
SendMessageW(hwnd, WM_SETICON, icon_type as usize, 0);
impl Pixel {
fn convert_to_bgra(&mut self) {
mem::swap(&mut self.r, &mut self.b);
}
}
#[derive(Debug, Clone, Copy)]
pub enum IconType {
Small = ICON_SMALL as isize,
Big = ICON_BIG as isize,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct RaiiIcon {
handle: HICON,
}
unsafe impl Send for RaiiIcon {}
unsafe impl Sync for RaiiIcon {}
impl Drop for RaiiIcon {
fn drop(&mut self) {
unsafe { DestroyIcon(self.handle) };
}
}

View file

@ -2,7 +2,7 @@ use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX};
pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
pub(crate) use self::icon::{SelectedCursor, WinIcon as PlatformIcon, WinIcon};
pub(crate) use self::icon::{RaiiIcon, SelectedCursor};
pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::Window;
@ -10,7 +10,7 @@ use crate::event::DeviceId;
use crate::icon::Icon;
use crate::platform::windows::{BackdropType, Color, CornerPreference};
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct PlatformSpecificWindowAttributes {
pub owner: Option<HWND>,
pub menu: Option<HMENU>,

View file

@ -37,14 +37,15 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, EnableMenuItem, FlashWindowEx, GetClientRect, GetCursorPos,
GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW,
GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
SetCursor, SetCursorPos, SetForegroundWindow, SetMenuDefaultItem, SetWindowDisplayAffinity,
SetWindowPlacement, SetWindowPos, SetWindowTextW, TrackPopupMenu, CS_HREDRAW, CS_VREDRAW,
CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY,
GWLP_HINSTANCE, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP,
HTTOPLEFT, HTTOPRIGHT, MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, NID_READY,
PM_NOREMOVE, SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE, SM_DIGITIZER,
SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN, TPM_RETURNCMD,
WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SYSCOMMAND, WNDCLASSEXW,
SendMessageW, SetCursor, SetCursorPos, SetForegroundWindow, SetMenuDefaultItem,
SetWindowDisplayAffinity, SetWindowPlacement, SetWindowPos, SetWindowTextW, TrackPopupMenu,
CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG,
FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT,
HTTOP, HTTOPLEFT, HTTOPRIGHT, MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND,
NID_READY, PM_NOREMOVE, SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE,
SM_DIGITIZER, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN,
TPM_RETURNCMD, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SETICON, WM_SYSCOMMAND,
WNDCLASSEXW,
};
use super::icon::WinCursor;
@ -52,9 +53,9 @@ use super::MonitorHandle;
use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::icon::Icon;
use crate::icon::{Icon, RgbaIcon};
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider};
use crate::platform::windows::{BackdropType, Color, CornerPreference};
use crate::platform::windows::{BackdropType, Color, CornerPreference, WinIcon};
use crate::platform_impl::platform::dark_mode::try_theme;
use crate::platform_impl::platform::definitions::{
CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, ITaskbarList, ITaskbarList2,
@ -66,7 +67,7 @@ use crate::platform_impl::platform::drop_handler::FileDropHandler;
use crate::platform_impl::platform::event_loop::{
self, ActiveEventLoop, Event, EventLoopRunner, DESTROY_MSG_ID,
};
use crate::platform_impl::platform::icon::{self, IconType};
use crate::platform_impl::platform::icon::IconType;
use crate::platform_impl::platform::ime::ImeContext;
use crate::platform_impl::platform::keyboard::KeyEventBuilder;
use crate::platform_impl::platform::window_state::{
@ -189,12 +190,11 @@ impl Window {
}
pub fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
if let Some(ref taskbar_icon) = taskbar_icon {
taskbar_icon.inner.set_for_window(self.hwnd(), IconType::Big);
if let Some(taskbar_icon) = taskbar_icon {
self.set_icon(taskbar_icon, IconType::Big);
} else {
icon::unset_for_window(self.hwnd(), IconType::Big);
self.unset_icon(IconType::Big);
}
self.window_state_lock().taskbar_icon = taskbar_icon;
}
unsafe fn handle_os_dragging(&self, wparam: WPARAM) {
@ -349,6 +349,45 @@ impl Window {
);
}
}
fn set_icon(&self, mut new_icon: Icon, icon_type: IconType) {
if let Some(icon) = new_icon.0.cast_ref::<RgbaIcon>() {
let icon = match WinIcon::from_rgba(icon) {
Ok(icon) => icon,
Err(err) => {
warn!("{}", err);
return;
},
};
new_icon = Icon(Arc::new(icon));
}
if let Some(icon) = new_icon.0.cast_ref::<WinIcon>() {
unsafe {
SendMessageW(
self.hwnd(),
WM_SETICON,
icon_type as usize,
icon.as_raw_handle() as isize,
);
}
match icon_type {
IconType::Small => self.window_state_lock().window_icon = Some(new_icon),
IconType::Big => self.window_state_lock().taskbar_icon = Some(new_icon),
}
}
}
fn unset_icon(&self, icon_type: IconType) {
unsafe {
SendMessageW(self.hwnd(), WM_SETICON, icon_type as usize, 0);
}
match icon_type {
IconType::Small => self.window_state_lock().window_icon = None,
IconType::Big => self.window_state_lock().taskbar_icon = None,
}
}
}
impl Drop for Window {
@ -974,12 +1013,11 @@ impl CoreWindow for Window {
}
fn set_window_icon(&self, window_icon: Option<Icon>) {
if let Some(ref window_icon) = window_icon {
window_icon.inner.set_for_window(self.hwnd(), IconType::Small);
if let Some(window_icon) = window_icon {
self.set_icon(window_icon, IconType::Small);
} else {
icon::unset_for_window(self.hwnd(), IconType::Small);
self.unset_icon(IconType::Small);
}
self.window_state_lock().window_icon = window_icon;
}
fn set_ime_cursor_area(&self, spot: Position, size: Size) {

View file

@ -46,7 +46,7 @@ impl fmt::Debug for WindowId {
}
/// Attributes used when creating a window.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub struct WindowAttributes {
pub surface_size: Option<Size>,
pub min_surface_size: Option<Size>,