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        match self {
63            BadIcon::OsError(e) => Some(e),
64            _ => None,
65        }
66    }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub(crate) struct RgbaIcon {
71    pub(crate) rgba: Vec<u8>,
72    pub(crate) width: u32,
73    pub(crate) height: u32,
74}
75
76/// For platforms which don't have window icons (e.g. web)
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub(crate) struct NoIcon;
79
80#[allow(dead_code)] // These are not used on every platform
81mod constructors {
82    use super::*;
83
84    impl RgbaIcon {
85        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
86            if rgba.len() % PIXEL_SIZE != 0 {
87                return Err(BadIcon::ByteCountNotDivisibleBy4 {
88                    byte_count: rgba.len(),
89                });
90            }
91            let pixel_count = rgba.len() / PIXEL_SIZE;
92            if pixel_count != (width * height) as usize {
93                Err(BadIcon::DimensionsVsPixelCount {
94                    width,
95                    height,
96                    width_x_height: (width * height) as usize,
97                    pixel_count,
98                })
99            } else {
100                Ok(RgbaIcon {
101                    rgba,
102                    width,
103                    height,
104                })
105            }
106        }
107    }
108
109    impl NoIcon {
110        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
111            // Create the rgba icon anyway to validate the input
112            let _ = RgbaIcon::from_rgba(rgba, width, height)?;
113            Ok(NoIcon)
114        }
115    }
116}
117
118/// An icon used for the window titlebar, taskbar, etc.
119#[derive(Clone)]
120pub struct Icon {
121    pub(crate) inner: PlatformIcon,
122}
123
124impl fmt::Debug for Icon {
125    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
126        fmt::Debug::fmt(&self.inner, formatter)
127    }
128}
129
130impl Icon {
131    /// Creates an icon from 32bpp RGBA data.
132    ///
133    /// The length of `rgba` must be divisible by 4, and `width * height` must equal
134    /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
135    pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
136        Ok(Icon {
137            inner: PlatformIcon::from_rgba(rgba, width, height)?,
138        })
139    }
140
141    /// Create an icon from a file path.
142    ///
143    /// Specify `size` to load a specific icon size from the file, or `None` to load the default
144    /// icon size from the file.
145    ///
146    /// In cases where the specified size does not exist in the file, Windows may perform scaling
147    /// to get an icon of the desired size.
148    #[cfg(windows)]
149    pub fn from_path<P: AsRef<std::path::Path>>(
150        path: P,
151        size: Option<(u32, u32)>,
152    ) -> Result<Self, BadIcon> {
153        let win_icon = PlatformIcon::from_path(path, size)?;
154        Ok(Icon { inner: win_icon })
155    }
156
157    /// Create an icon from a resource embedded in this executable or library.
158    ///
159    /// Specify `size` to load a specific icon size from the file, or `None` to load the default
160    /// icon size from the file.
161    ///
162    /// In cases where the specified size does not exist in the file, Windows may perform scaling
163    /// to get an icon of the desired size.
164    #[cfg(windows)]
165    pub fn from_resource(ordinal: u16, size: Option<(u32, u32)>) -> Result<Self, BadIcon> {
166        let win_icon = PlatformIcon::from_resource(ordinal, size)?;
167        Ok(Icon { inner: win_icon })
168    }
169
170    /// This is basically the same as from_resource, but takes a resource name
171    /// rather than oridinal id.
172    #[cfg(windows)]
173    pub fn from_resource_name(
174        resource_name: &str,
175        size: Option<(u32, u32)>,
176    ) -> Result<Self, BadIcon> {
177        let win_icon = PlatformIcon::from_resource_name(resource_name, size)?;
178        Ok(Icon { inner: win_icon })
179    }
180
181    /// Create an icon from an HICON
182    #[cfg(windows)]
183    pub fn from_handle(handle: isize) -> Self {
184        let win_icon = PlatformIcon::from_handle(handle as _);
185        Icon { inner: win_icon }
186    }
187}