use crate::platform_impl::PlatformIcon;
use std::{error::Error, fmt, io, mem};
#[repr(C)]
#[derive(Debug)]
pub(crate) struct Pixel {
pub(crate) r: u8,
pub(crate) g: u8,
pub(crate) b: u8,
pub(crate) a: u8,
}
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
#[derive(Debug)]
pub enum BadIcon {
ByteCountNotDivisibleBy4 { byte_count: usize },
DimensionsVsPixelCount {
width: u32,
height: u32,
width_x_height: usize,
pixel_count: usize,
},
OsError(io::Error),
}
impl fmt::Display for BadIcon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
"The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
byte_count,
),
BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height,
pixel_count,
} => write!(f,
"The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.",
width, height, pixel_count, width_x_height,
),
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e),
}
}
}
impl Error for BadIcon {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RgbaIcon {
pub(crate) rgba: Vec<u8>,
pub(crate) width: u32,
pub(crate) height: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct NoIcon;
#[allow(dead_code)] mod constructors {
use super::*;
impl RgbaIcon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if !rgba.len().is_multiple_of(PIXEL_SIZE) {
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> {
let _ = RgbaIcon::from_rgba(rgba, width, height)?;
Ok(NoIcon)
}
}
}
#[derive(Clone)]
pub struct Icon {
pub(crate) inner: PlatformIcon,
#[cfg(feature = "snapshot")]
pub(crate) rgba: Option<RgbaIcon>,
}
impl fmt::Debug for Icon {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.inner, formatter)
}
}
impl Icon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
#[cfg(feature = "snapshot")]
{
let rgba = RgbaIcon::from_rgba(rgba, width, height)?;
Ok(Icon {
inner: PlatformIcon::from_rgba(rgba.rgba.clone(), width, height)?,
rgba: Some(rgba),
})
}
#[cfg(not(feature = "snapshot"))]
{
Ok(Icon {
inner: PlatformIcon::from_rgba(rgba, width, height)?,
})
}
}
#[cfg(windows)]
pub fn from_path<P: AsRef<std::path::Path>>(
path: P,
size: Option<(u32, u32)>,
) -> Result<Self, BadIcon> {
let win_icon = PlatformIcon::from_path(path, size)?;
Ok(Icon {
inner: win_icon,
#[cfg(feature = "snapshot")]
rgba: None,
})
}
#[cfg(windows)]
pub fn from_resource(ordinal: u16, size: Option<(u32, u32)>) -> Result<Self, BadIcon> {
let win_icon = PlatformIcon::from_resource(ordinal, size)?;
Ok(Icon {
inner: win_icon,
#[cfg(feature = "snapshot")]
rgba: None,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(windows, derive(Copy))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NativeIcon {
Add,
Advanced,
Bluetooth,
Bookmarks,
Caution,
ColorPanel,
ColumnView,
Computer,
EnterFullScreen,
Everyone,
ExitFullScreen,
FlowView,
Folder,
FolderBurnable,
FolderSmart,
FollowLinkFreestanding,
FontPanel,
GoLeft,
GoRight,
Home,
IChatTheater,
IconView,
Info,
InvalidDataFreestanding,
LeftFacingTriangle,
ListView,
LockLocked,
LockUnlocked,
MenuMixedState,
MenuOnState,
MobileMe,
MultipleDocuments,
Network,
Path,
PreferencesGeneral,
QuickLook,
RefreshFreestanding,
Refresh,
Remove,
RevealFreestanding,
RightFacingTriangle,
Share,
Slideshow,
SmartBadge,
StatusAvailable,
StatusNone,
StatusPartiallyAvailable,
StatusUnavailable,
StopProgressFreestanding,
StopProgress,
TrashEmpty,
TrashFull,
User,
UserAccounts,
UserGroup,
UserGuest,
#[cfg(windows)]
Raw(i32),
#[cfg(not(windows))]
Raw(String),
}
impl NativeIcon {
#[cfg(windows)]
pub fn from_id(id: i32) -> Self {
Self::Raw(id)
}
#[cfg(not(windows))]
pub fn from_name<S: Into<String>>(icon: S) -> Self {
Self::Raw(icon.into())
}
}
#[cfg(windows)]
impl From<i32> for NativeIcon {
fn from(id: i32) -> Self {
Self::from_id(id)
}
}
#[cfg(not(windows))]
impl From<String> for NativeIcon {
fn from(icon: String) -> Self {
Self::from_name(icon)
}
}
#[cfg(not(windows))]
impl From<&str> for NativeIcon {
fn from(icon: &str) -> Self {
Self::from_name(icon)
}
}
impl crate::NativeIcon {
pub fn freedesktop_name(&self) -> &str {
match self {
Self::Add => "list-add-symbolic",
Self::Advanced => "preferences-system-symbolic",
Self::Bluetooth => "bluetooth-symbolic",
Self::Bookmarks => "user-bookmarks-symbolic",
Self::Caution => "dialog-warning-symbolic",
Self::ColorPanel => "applications-graphics-symbolic",
Self::ColumnView => "view-list-symbolic",
Self::Computer => "computer-symbolic",
Self::EnterFullScreen => "view-fullscreen-symbolic",
Self::Everyone => "system-users-symbolic",
Self::ExitFullScreen => "view-restore-symbolic",
Self::FlowView => "view-grid-symbolic",
Self::Folder => "folder-symbolic",
Self::FolderBurnable => "media-optical-symbolic",
Self::FolderSmart => "folder-saved-search-symbolic",
Self::FollowLinkFreestanding => "insert-link-symbolic",
Self::FontPanel => "preferences-desktop-font-symbolic",
Self::GoLeft => "go-previous-symbolic",
Self::GoRight => "go-next-symbolic",
Self::Home => "user-home-symbolic",
Self::IChatTheater => "camera-video-symbolic",
Self::IconView => "view-grid-symbolic",
Self::Info => "dialog-information-symbolic",
Self::InvalidDataFreestanding => "dialog-error-symbolic",
Self::LeftFacingTriangle => "pan-start-symbolic",
Self::ListView => "view-list-symbolic",
Self::LockLocked => "changes-prevent-symbolic",
Self::LockUnlocked => "changes-allow-symbolic",
Self::MenuMixedState => "list-remove-symbolic",
Self::MenuOnState => "object-select-symbolic",
Self::MobileMe => "network-server-symbolic",
Self::MultipleDocuments => "edit-copy-symbolic",
Self::Network => "network-workgroup-symbolic",
Self::Path => "document-open-recent-symbolic",
Self::PreferencesGeneral => "preferences-system-symbolic",
Self::QuickLook => "document-preview-symbolic",
Self::RefreshFreestanding | Self::Refresh => "view-refresh-symbolic",
Self::Remove => "list-remove-symbolic",
Self::RevealFreestanding => "folder-open-symbolic",
Self::RightFacingTriangle => "pan-end-symbolic",
Self::Share => "emblem-shared-symbolic",
Self::Slideshow => "view-presentation-symbolic",
Self::SmartBadge => "emblem-favorite-symbolic",
Self::StatusAvailable => "user-available-symbolic",
Self::StatusNone => "user-offline-symbolic",
Self::StatusPartiallyAvailable => "user-idle-symbolic",
Self::StatusUnavailable => "user-busy-symbolic",
Self::StopProgressFreestanding | Self::StopProgress => "process-stop-symbolic",
Self::TrashEmpty => "user-trash-symbolic",
Self::TrashFull => "user-trash-full-symbolic",
Self::User => "avatar-default-symbolic",
Self::UserAccounts | Self::UserGroup => "system-users-symbolic",
Self::UserGuest => "avatar-default-symbolic",
#[cfg(not(windows))]
Self::Raw(name) => name,
#[cfg(windows)]
Self::Raw(_) => "unknown",
}
}
}