// Copyright 2022 System76 // SPDX-License-Identifier: MPL-2.0 //! Lazily-generated SVG icon widget for Iced. mod bundle; mod named; use std::sync::Arc; pub use named::{IconFallback, Named}; mod handle; pub use handle::{Data, Handle, from_path, from_raster_bytes, from_raster_pixels, from_svg_bytes}; use crate::Element; use derive_setters::Setters; use iced::widget::{Image, Svg}; use iced::{ContentFit, Length, Radians, Rectangle}; use iced_core::Rotation; /// Create an [`Icon`] from a pre-existing [`Handle`] pub fn icon(handle: Handle) -> Icon { Icon { content_fit: ContentFit::Fill, handle, height: None, size: 16, class: crate::theme::Svg::default(), rotation: None, width: None, } } /// Create an icon handle from its XDG icon name. pub fn from_name(name: impl Into>) -> Named { Named::new(name) } /// An image which may be an SVG or PNG. #[must_use] #[derive(Clone, Setters)] pub struct Icon { #[setters(skip)] handle: Handle, class: crate::theme::Svg, #[setters(skip)] pub(super) size: u16, content_fit: ContentFit, #[setters(strip_option)] width: Option, #[setters(strip_option)] height: Option, #[setters(strip_option)] rotation: Option, } impl Icon { #[must_use] pub fn into_svg_handle(self) -> Option { match self.handle.data { Data::Image(_) => (), Data::Svg(handle) => return Some(handle), } None } #[must_use] pub fn size(mut self, size: u16) -> Self { self.size = size; self } #[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))), ) .rotation(self.rotation.unwrap_or_default()) .content_fit(self.content_fit) .into() }; let from_svg = |handle| { Svg::::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))), ) .rotation(self.rotation.unwrap_or_default()) .content_fit(self.content_fit) .symbolic(self.handle.symbolic) .into() }; match self.handle.data { Data::Image(handle) => from_image(handle), Data::Svg(handle) => from_svg(handle), } } } impl<'a, Message: 'a> From for Element<'a, Message> { fn from(icon: Icon) -> Self { icon.view::() } } /// Draw an icon in the given bounds via the runtime's renderer. pub fn draw(renderer: &mut crate::Renderer, handle: &Handle, icon_bounds: Rectangle) { match handle.clone().data { Data::Svg(handle) => iced_core::svg::Renderer::draw_svg( renderer, iced_core::svg::Svg::new(handle), icon_bounds, icon_bounds, ), Data::Image(handle) => { iced_core::image::Renderer::draw_image( renderer, iced_core::Image { handle, filter_method: iced_core::image::FilterMethod::Linear, rotation: Radians(0.), border_radius: [0.0; 4].into(), opacity: 1.0, snap: true, }, icon_bounds, icon_bounds, ); } } }