tray_icon/
icon.rs

1// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5// taken from https://github.com/rust-windowing/winit/blob/92fdf5ba85f920262a61cee4590f4a11ad5738d1/src/icon.rs
6
7use crate::platform_impl::PlatformIcon;
8use std::{error::Error, fmt, io, mem};
9
10#[repr(C)]
11#[derive(Debug)]
12pub(crate) struct Pixel {
13    pub(crate) r: u8,
14    pub(crate) g: u8,
15    pub(crate) b: u8,
16    pub(crate) a: u8,
17}
18
19pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
20
21#[derive(Debug)]
22/// An error produced when using [`Icon::from_rgba`] with invalid arguments.
23pub enum BadIcon {
24    /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
25    /// safely interpreted as 32bpp RGBA pixels.
26    ByteCountNotDivisibleBy4 { byte_count: usize },
27    /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
28    /// At least one of your arguments is incorrect.
29    DimensionsVsPixelCount {
30        width: u32,
31        height: u32,
32        width_x_height: usize,
33        pixel_count: usize,
34    },
35    /// Produced when underlying OS functionality failed to create the icon
36    OsError(io::Error),
37}
38
39impl fmt::Display for BadIcon {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
43                "The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
44                byte_count,
45            ),
46            BadIcon::DimensionsVsPixelCount {
47                width,
48                height,
49                width_x_height,
50                pixel_count,
51            } => write!(f,
52                "The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.",
53                width, height, pixel_count, width_x_height,
54            ),
55            BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e),
56        }
57    }
58}
59
60impl Error for BadIcon {
61    fn source(&self) -> Option<&(dyn Error + 'static)> {
62        Some(self)
63    }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub(crate) struct RgbaIcon {
68    pub(crate) rgba: Vec<u8>,
69    pub(crate) width: u32,
70    pub(crate) height: u32,
71}
72
73/// For platforms which don't have window icons (e.g. web)
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub(crate) struct NoIcon;
76
77#[allow(dead_code)] // These are not used on every platform
78mod constructors {
79    use super::*;
80
81    impl RgbaIcon {
82        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
83            if rgba.len() % PIXEL_SIZE != 0 {
84                return Err(BadIcon::ByteCountNotDivisibleBy4 {
85                    byte_count: rgba.len(),
86                });
87            }
88            let pixel_count = rgba.len() / PIXEL_SIZE;
89            if pixel_count != (width * height) as usize {
90                Err(BadIcon::DimensionsVsPixelCount {
91                    width,
92                    height,
93                    width_x_height: (width * height) as usize,
94                    pixel_count,
95                })
96            } else {
97                Ok(RgbaIcon {
98                    rgba,
99                    width,
100                    height,
101                })
102            }
103        }
104    }
105
106    impl NoIcon {
107        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
108            // Create the rgba icon anyway to validate the input
109            let _ = RgbaIcon::from_rgba(rgba, width, height)?;
110            Ok(NoIcon)
111        }
112    }
113}
114
115/// An icon used for the window titlebar, taskbar, etc.
116#[derive(Clone)]
117pub struct Icon {
118    pub(crate) inner: PlatformIcon,
119}
120
121impl fmt::Debug for Icon {
122    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
123        fmt::Debug::fmt(&self.inner, formatter)
124    }
125}
126
127impl Icon {
128    /// Creates an icon from 32bpp RGBA data.
129    ///
130    /// The length of `rgba` must be divisible by 4, and `width * height` must equal
131    /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
132    pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
133        Ok(Icon {
134            inner: PlatformIcon::from_rgba(rgba, width, height)?,
135        })
136    }
137
138    /// Create an icon from a file path.
139    ///
140    /// Specify `size` to load a specific icon size from the file, or `None` to load the default
141    /// icon size from the file.
142    ///
143    /// In cases where the specified size does not exist in the file, Windows may perform scaling
144    /// to get an icon of the desired size.
145    #[cfg(windows)]
146    pub fn from_path<P: AsRef<std::path::Path>>(
147        path: P,
148        size: Option<(u32, u32)>,
149    ) -> Result<Self, BadIcon> {
150        let win_icon = PlatformIcon::from_path(path, size)?;
151        Ok(Icon { inner: win_icon })
152    }
153
154    /// Create an icon from a resource embedded in this executable or library.
155    ///
156    /// Specify `size` to load a specific icon size from the file, or `None` to load the default
157    /// icon size from the file.
158    ///
159    /// In cases where the specified size does not exist in the file, Windows may perform scaling
160    /// to get an icon of the desired size.
161    #[cfg(windows)]
162    pub fn from_resource(ordinal: u16, size: Option<(u32, u32)>) -> Result<Self, BadIcon> {
163        let win_icon = PlatformIcon::from_resource(ordinal, size)?;
164        Ok(Icon { inner: win_icon })
165    }
166
167    /// This is basically the same as from_resource, but takes a resource name
168    /// rather than oridinal id.
169    #[cfg(windows)]
170    pub fn from_resource_name(
171        resource_name: &str,
172        size: Option<(u32, u32)>,
173    ) -> Result<Self, BadIcon> {
174        let win_icon = PlatformIcon::from_resource_name(resource_name, size)?;
175        Ok(Icon { inner: win_icon })
176    }
177
178    /// Create an icon from an HICON
179    #[cfg(windows)]
180    pub fn from_handle(handle: isize) -> Self {
181        let win_icon = PlatformIcon::from_handle(handle as _);
182        Icon { inner: win_icon }
183    }
184}