refactor: add IconSource type & force_svg option

This commit is contained in:
Ashley Wulber 2022-12-16 19:17:37 -05:00
parent 38f7af6c07
commit 70c3f0b394
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
3 changed files with 77 additions and 17 deletions

View file

@ -95,6 +95,7 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> {
let icon = |name, size, on_press| { let icon = |name, size, on_press| {
super::icon(name, size) super::icon(name, size)
.force_svg(true)
.style(crate::theme::Svg::SymbolicActive) .style(crate::theme::Svg::SymbolicActive)
.apply(iced::widget::button) .apply(iced::widget::button)
.style(theme::Button::Text) .style(theme::Button::Text)

View file

@ -7,17 +7,54 @@ use iced::{
widget::{svg, Image}, widget::{svg, Image},
Length, ContentFit, Length, ContentFit,
}; };
use std::{borrow::Cow, ffi::OsStr}; use std::{borrow::{Cow, Borrow}, ffi::OsStr, path::Path};
use std::hash::Hash; use std::hash::Hash;
use std::rc::Rc; use std::rc::Rc;
use derive_setters::Setters; use derive_setters::Setters;
use crate::{Element, Renderer}; use crate::{Element, Renderer};
#[derive(Debug, Hash)]
pub enum IconSource<'a> {
Path(&'a Path),
Name(Cow<'a, str>),
Embedded(Image),
}
impl<'a> From<&'a Path> for IconSource<'a> {
fn from(value: &'a Path) -> Self {
Self::Path(value)
}
}
impl<'a> From<Cow<'a, str>> for IconSource<'a> {
fn from(value: Cow<'a, str>) -> Self {
Self::Name(value)
}
}
impl<'a> From<String> for IconSource<'a> {
fn from(value: String) -> Self {
Self::Name(value.into())
}
}
impl<'a> From<&'a str> for IconSource<'a> {
fn from(value: &'a str) -> Self {
Self::Name(value.into())
}
}
impl<'a> From<Image> for IconSource<'a> {
fn from(value: Image) -> Self {
Self::Embedded(value)
}
}
/// A lazily-generated icon. /// A lazily-generated icon.
#[derive(Hash, Setters)] #[derive(Hash, Setters)]
pub struct Icon<'a> { pub struct Icon<'a> {
#[setters(skip)] #[setters(skip)]
name: Cow<'a, str>, name: IconSource<'a>,
#[setters(into)] #[setters(into)]
theme: Cow<'a, str>, theme: Cow<'a, str>,
style: crate::theme::Svg, style: crate::theme::Svg,
@ -28,11 +65,13 @@ pub struct Icon<'a> {
width: Option<Length>, width: Option<Length>,
#[setters(strip_option)] #[setters(strip_option)]
height: Option<Length>, height: Option<Length>,
force_svg: bool,
} }
/// A lazily-generated icon. /// A lazily-generated icon.
#[must_use] #[must_use]
pub fn icon<'a>(name: impl Into<Cow<'a, str>>, size: u16) -> Icon<'a> { pub fn icon<'a>(name: impl Into<IconSource<'a>>, size: u16) -> Icon<'a> {
Icon { Icon {
content_fit: None, content_fit: None,
height: None, height: None,
@ -41,35 +80,54 @@ pub fn icon<'a>(name: impl Into<Cow<'a, str>>, size: u16) -> Icon<'a> {
style: crate::theme::Svg::default(), style: crate::theme::Svg::default(),
theme: Cow::Borrowed("Pop"), theme: Cow::Borrowed("Pop"),
width: None, width: None,
force_svg: false
} }
} }
impl<'a> Icon<'a> { impl<'a> Icon<'a> {
#[must_use] #[must_use]
fn into_element<Message: 'static>(self) -> Element<'a, Message> { fn into_element<Message: 'static>(self) -> Element<'a, Message> {
if let IconSource::Embedded(mut image) = self.name {
image = image
.width(self.width.unwrap_or(Length::Units(self.size)))
.height(self.height.unwrap_or(Length::Units(self.size)));
if let Some(content_fit) = self.content_fit {
image = image.content_fit(content_fit);
}
return image.into();
}
let element = Rc::new(self); let element = Rc::new(self);
let element_clone = Rc::clone(&element); let element_clone = Rc::clone(&element);
iced_lazy::lazy(element_clone, move || -> Element<Message> { iced_lazy::lazy(element_clone, move || -> Element<Message> {
let icon = freedesktop_icons::lookup(&element.name) let icon = match &element.name {
IconSource::Path(path) => Some(Cow::from(*path)),
IconSource::Name(name) => {
let icon = freedesktop_icons::lookup(&name)
.with_size(element.size) .with_size(element.size)
.with_theme(&element.theme) .with_theme(&element.theme)
.with_cache() .with_cache()
.find(); .find();
let icon = if icon.is_none() { if icon.is_none() {
freedesktop_icons::lookup(&element.name) freedesktop_icons::lookup(&name)
.with_size(element.size) .with_size(element.size)
.with_cache() .with_cache()
.find() .find()
} else { } else {
icon icon
}.map(|p| Cow::from(p))
},
IconSource::Embedded(_) => unimplemented!(),
}; };
if icon.as_ref().map(|path| path.extension() == Some(&OsStr::new("svg"))).unwrap_or(true) { let is_svg = element.force_svg || icon.as_ref().map(|path| path.extension() == Some(&OsStr::new("svg"))).unwrap_or(true);
if is_svg {
let handle = if let Some(path) = icon { let handle = if let Some(path) = icon {
svg::Handle::from_path(path) svg::Handle::from_path(path)
} else { } else {
eprintln!("icon '{}' size {} not found", element.name, element.size); eprintln!("icon '{:?}' size {} not found", &element.name, element.size);
svg::Handle::from_memory(Vec::new()) svg::Handle::from_memory(Vec::new())
}; };

View file

@ -39,6 +39,7 @@ impl<'a, Message: 'static + Clone> From<NavButton<'a, Message>> for Element<'a,
}, },
24, 24,
) )
.force_svg(true)
.style(theme::Svg::SymbolicActive) .style(theme::Svg::SymbolicActive)
.width(Length::Units(24)) .width(Length::Units(24))
.height(Length::Fill); .height(Length::Fill);