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,
iced::widget::{checkbox, column, pick_list, progress_bar, radio, slider, text, text_input},
iced::{id, Alignment, Length},
theme::{self, Button as ButtonTheme, Theme, ThemeType},
theme::{self, Button as ButtonTheme, ThemeType},
widget::{
button, container, icon, segmented_button, segmented_selection, settings, spin_button,
toggler, view_switcher,
@ -417,11 +417,19 @@ impl State {
.padding(8)
.width(Length::Fill)
.into(),
container(text("Primary container with some text").size(24))
.layer(cosmic_theme::Layer::Primary)
.padding(8)
.width(Length::Fill)
.into(),
container(column![
text(
"Primary container with some text and a couple icons testing default fallbacks"
)
.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))
.layer(cosmic_theme::Layer::Secondary)
.padding(8)

View file

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