cosmic/widget/icon/
mod.rsmod named;
use std::ffi::OsStr;
use std::sync::Arc;
pub use named::{IconFallback, Named};
mod handle;
pub use handle::{from_path, from_raster_bytes, from_raster_pixels, from_svg_bytes, Data, Handle};
use crate::Element;
use derive_setters::Setters;
use iced::widget::{Image, Svg};
use iced::{ContentFit, Length, Rectangle};
pub fn icon(handle: Handle) -> Icon {
Icon {
content_fit: ContentFit::Fill,
handle,
height: None,
size: 16,
class: crate::theme::Svg::default(),
width: None,
}
}
pub fn from_name(name: impl Into<Arc<str>>) -> Named {
Named::new(name)
}
#[must_use]
#[derive(Clone, Setters)]
pub struct Icon {
#[setters(skip)]
handle: Handle,
class: crate::theme::Svg,
pub(super) size: u16,
content_fit: ContentFit,
#[setters(strip_option)]
width: Option<Length>,
#[setters(strip_option)]
height: Option<Length>,
}
impl Icon {
#[must_use]
pub fn into_svg_handle(self) -> Option<crate::widget::svg::Handle> {
match self.handle.data {
Data::Name(named) => {
if let Some(path) = named.path() {
if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
return Some(iced_core::svg::Handle::from_path(path));
}
}
}
Data::Image(_) => (),
Data::Svg(handle) => return Some(handle),
}
None
}
#[must_use]
fn view<'a, Message: 'a>(self) -> Element<'a, Message> {
let from_image = |handle| {
Image::new(handle)
.width(
self.width
.unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
)
.height(
self.height
.unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
)
.content_fit(self.content_fit)
.into()
};
let from_svg = |handle| {
Svg::<crate::Theme>::new(handle)
.class(self.class.clone())
.width(
self.width
.unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
)
.height(
self.height
.unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
)
.content_fit(self.content_fit)
.symbolic(self.handle.symbolic)
.into()
};
match self.handle.data {
Data::Name(named) => {
if let Some(path) = named.path() {
if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
from_svg(iced_core::svg::Handle::from_path(path))
} else {
from_image(iced_core::image::Handle::from_path(path))
}
} else {
let bytes: &'static [u8] = &[];
from_svg(iced_core::svg::Handle::from_memory(bytes))
}
}
Data::Image(handle) => from_image(handle),
Data::Svg(handle) => from_svg(handle),
}
}
}
impl<'a, Message: 'a> From<Icon> for Element<'a, Message> {
fn from(icon: Icon) -> Self {
icon.view::<Message>()
}
}
pub fn draw(renderer: &mut crate::Renderer, handle: &Handle, icon_bounds: Rectangle) {
enum IcedHandle {
Svg(iced_core::svg::Handle),
Image(iced_core::image::Handle),
}
let iced_handle = match handle.clone().data {
Data::Name(named) => named.path().map(|path| {
if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
IcedHandle::Svg(iced_core::svg::Handle::from_path(path))
} else {
IcedHandle::Image(iced_core::image::Handle::from_path(path))
}
}),
Data::Image(handle) => Some(IcedHandle::Image(handle)),
Data::Svg(handle) => Some(IcedHandle::Svg(handle)),
};
match iced_handle {
Some(IcedHandle::Svg(handle)) => iced_core::svg::Renderer::draw_svg(
renderer,
iced_core::svg::Svg::new(handle),
icon_bounds,
),
Some(IcedHandle::Image(handle)) => {
iced_core::image::Renderer::draw_image(
renderer,
handle,
iced_core::image::FilterMethod::Linear,
icon_bounds,
iced_core::Radians::from(0),
1.0,
[0.0; 4],
);
}
None => {}
}
}