feat: icon default fallbacks

This commit is contained in:
Ashley Wulber 2023-07-07 16:39:22 -04:00 committed by Ashley Wulber
parent 456b2ddcd5
commit 598bfaa611
3 changed files with 67 additions and 30 deletions

View file

@ -3,7 +3,7 @@ use cosmic::{
cosmic_theme, cosmic_theme,
iced::widget::{checkbox, column, pick_list, progress_bar, radio, slider, text, text_input}, iced::widget::{checkbox, column, pick_list, progress_bar, radio, slider, text, text_input},
iced::{id, Alignment, Length}, iced::{id, Alignment, Length},
theme::{self, Button as ButtonTheme, Theme, ThemeType}, theme::{self, Button as ButtonTheme, ThemeType},
widget::{ widget::{
button, container, icon, segmented_button, segmented_selection, settings, spin_button, button, container, icon, segmented_button, segmented_selection, settings, spin_button,
toggler, view_switcher, toggler, view_switcher,
@ -417,11 +417,19 @@ impl State {
.padding(8) .padding(8)
.width(Length::Fill) .width(Length::Fill)
.into(), .into(),
container(text("Primary container with some text").size(24)) container(column![
.layer(cosmic_theme::Layer::Primary) text(
.padding(8) "Primary container with some text and a couple icons testing default fallbacks"
.width(Length::Fill) )
.into(), .size(24),
icon("microphone-sensitivity-high-symbolic-test", 24)
.style(cosmic::theme::Svg::SymbolicActive),
icon("microphone-sensitivity-high-symbolic-test", 16).default_fallbacks(false)
])
.layer(cosmic_theme::Layer::Primary)
.padding(8)
.width(Length::Fill)
.into(),
container(text("Secondary container with some text").size(24)) container(text("Secondary container with some text").size(24))
.layer(cosmic_theme::Layer::Secondary) .layer(cosmic_theme::Layer::Secondary)
.padding(8) .padding(8)

View file

@ -13,6 +13,7 @@ use std::{
borrow::Cow, collections::hash_map::DefaultHasher, ffi::OsStr, hash::Hash, hash::Hasher, borrow::Cow, collections::hash_map::DefaultHasher, ffi::OsStr, hash::Hash, hash::Hasher,
path::Path, path::PathBuf, path::Path, path::PathBuf,
}; };
use tracing::error;
#[derive(Clone, Debug, Hash)] #[derive(Clone, Debug, Hash)]
pub enum Handle { pub enum Handle {
@ -30,30 +31,30 @@ pub enum IconSource<'a> {
impl<'a> IconSource<'a> { impl<'a> IconSource<'a> {
/// Loads the icon as either an image or svg [`Handle`]. /// Loads the icon as either an image or svg [`Handle`].
#[must_use] #[must_use]
pub fn load(&self, size: u16, theme: Option<&str>, svg: bool) -> Handle { pub fn load(
let name_path_buffer: Option<PathBuf>; &self,
size: u16,
theme: Option<&str>,
svg: bool,
default_fallbacks: bool,
) -> Handle {
let mut name_path_buffer: Option<PathBuf>;
let icon: Option<&Path> = match self { let icon: Option<&Path> = match self {
IconSource::Handle(handle) => return handle.clone(), IconSource::Handle(handle) => return handle.clone(),
IconSource::Path(ref path) => Some(path), IconSource::Path(ref path) => Some(path),
#[cfg(unix)] #[cfg(unix)]
IconSource::Name(ref name) => { IconSource::Name(ref name) => {
let icon = crate::settings::DEFAULT_ICON_THEME.with(|default_theme| { name_path_buffer = None;
let default_theme: &str = &default_theme.borrow(); if let Some(path) = load_icon(name, size, theme) {
freedesktop_icons::lookup(name) name_path_buffer = Some(path);
.with_size(size) } else if default_fallbacks {
.with_theme(theme.unwrap_or(default_theme)) for name in name.rmatch_indices('-').map(|(pos, _)| &name[..pos]) {
.with_cache() if let Some(path) = load_icon(name, size, theme) {
.find() name_path_buffer = Some(path);
}); break;
}
name_path_buffer = if icon.is_none() { }
freedesktop_icons::lookup(name) }
.with_size(size)
.with_cache()
.find()
} else {
icon
};
name_path_buffer.as_deref() name_path_buffer.as_deref()
} }
@ -71,7 +72,7 @@ impl<'a> IconSource<'a> {
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!("svg icon '{self:?}' size {size} not found"); error!("svg icon '{self:?}' size {size} not found");
svg::Handle::from_memory(Vec::new()) svg::Handle::from_memory(Vec::new())
}; };
@ -79,7 +80,7 @@ impl<'a> IconSource<'a> {
} else if let Some(icon) = icon { } else if let Some(icon) = icon {
Handle::Image(icon.into()) Handle::Image(icon.into())
} else { } else {
eprintln!("icon '{self:?}' size {size} not found"); error!("icon '{self:?}' size {size} not found");
Handle::Image(image::Handle::from_memory(Vec::new())) Handle::Image(image::Handle::from_memory(Vec::new()))
} }
} }
@ -189,6 +190,7 @@ pub struct Icon<'a> {
#[setters(strip_option)] #[setters(strip_option)]
height: Option<Length>, height: Option<Length>,
force_svg: bool, force_svg: bool,
default_fallbacks: bool,
} }
// XXX Hopefully this will be enough precision // XXX Hopefully this will be enough precision
@ -230,6 +232,7 @@ pub fn icon<'a>(source: impl Into<IconSource<'a>>, size: u16) -> Icon<'a> {
theme: None, theme: None,
width: None, width: None,
force_svg: false, force_svg: false,
default_fallbacks: true,
} }
} }
@ -266,7 +269,12 @@ impl<'a> Icon<'a> {
std::mem::swap(&mut source, &mut self.source); std::mem::swap(&mut source, &mut self.source);
iced::widget::lazy(hash, move |_| -> Element<Message> { iced::widget::lazy(hash, move |_| -> Element<Message> {
match source.load(self.size, self.theme.as_deref(), self.force_svg) { match source.load(
self.size,
self.theme.as_deref(),
self.force_svg,
self.default_fallbacks,
) {
Handle::Svg(handle) => self.svg_element(handle), Handle::Svg(handle) => self.svg_element(handle),
Handle::Image(handle) => self.raster_element(handle), Handle::Image(handle) => self.raster_element(handle),
} }
@ -280,3 +288,24 @@ impl<'a, Message: 'static> From<Icon<'a>> for Element<'a, Message> {
icon.into_element::<Message>() icon.into_element::<Message>()
} }
} }
#[must_use]
pub fn load_icon(name: &str, size: u16, theme: Option<&str>) -> Option<PathBuf> {
let icon = crate::settings::DEFAULT_ICON_THEME.with(|default_theme| {
let default_theme = default_theme.borrow();
freedesktop_icons::lookup(name)
.with_size(size)
.with_theme(theme.unwrap_or(&default_theme))
.with_cache()
.find()
});
if icon.is_none() {
freedesktop_icons::lookup(name)
.with_size(size)
.with_cache()
.find()
} else {
icon
}
}

View file

@ -542,7 +542,7 @@ where
bounds.x += offset; bounds.x += offset;
bounds.width -= offset; bounds.width -= offset;
match icon.load(self.icon_size, None, false) { match icon.load(self.icon_size, None, false, true) {
icon::Handle::Image(_handle) => { icon::Handle::Image(_handle) => {
unimplemented!() unimplemented!()
} }
@ -583,7 +583,7 @@ where
let width = f32::from(self.icon_size); let width = f32::from(self.icon_size);
let icon_bounds = close_bounds(original_bounds, width, self.button_padding); let icon_bounds = close_bounds(original_bounds, width, self.button_padding);
match self.close_icon.load(self.icon_size, None, false) { match self.close_icon.load(self.icon_size, None, false, true) {
icon::Handle::Image(_handle) => { icon::Handle::Image(_handle) => {
unimplemented!() unimplemented!()
} }