refactor(widget): improvements to button and icon widgets
This commit is contained in:
parent
7f0943924a
commit
9dbc1be269
20 changed files with 399 additions and 558 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{Builder, Icon};
|
||||
use super::{Icon, Named};
|
||||
use crate::widget::{image, svg};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::OsStr;
|
||||
|
|
@ -13,7 +13,7 @@ use std::path::PathBuf;
|
|||
pub struct Handle {
|
||||
pub symbolic: bool,
|
||||
#[setters(skip)]
|
||||
pub variant: Variant,
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
|
|
@ -24,16 +24,12 @@ impl Handle {
|
|||
|
||||
#[must_use]
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub enum Variant {
|
||||
pub enum Data {
|
||||
Name(Named),
|
||||
Image(image::Handle),
|
||||
Svg(svg::Handle),
|
||||
}
|
||||
|
||||
/// Create an icon handle from its XDG icon name.
|
||||
pub fn from_name(name: &str) -> Builder {
|
||||
Builder::new(name)
|
||||
}
|
||||
|
||||
/// Create an icon handle from its path.
|
||||
pub fn from_path(path: PathBuf) -> Handle {
|
||||
Handle {
|
||||
|
|
@ -41,10 +37,10 @@ pub fn from_path(path: PathBuf) -> Handle {
|
|||
.file_stem()
|
||||
.and_then(OsStr::to_str)
|
||||
.is_some_and(|name| name.ends_with("-symbolic")),
|
||||
variant: if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
|
||||
Variant::Svg(svg::Handle::from_path(path))
|
||||
data: if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
|
||||
Data::Svg(svg::Handle::from_path(path))
|
||||
} else {
|
||||
Variant::Image(image::Handle::from_path(path))
|
||||
Data::Image(image::Handle::from_path(path))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +55,7 @@ pub fn from_raster_bytes(
|
|||
) -> Handle {
|
||||
Handle {
|
||||
symbolic: false,
|
||||
variant: Variant::Image(image::Handle::from_memory(bytes)),
|
||||
data: Data::Image(image::Handle::from_memory(bytes)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +71,7 @@ pub fn from_raster_pixels(
|
|||
) -> Handle {
|
||||
Handle {
|
||||
symbolic: false,
|
||||
variant: Variant::Image(image::Handle::from_pixels(width, height, pixels)),
|
||||
data: Data::Image(image::Handle::from_pixels(width, height, pixels)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +79,6 @@ pub fn from_raster_pixels(
|
|||
pub fn from_svg_bytes(bytes: impl Into<Cow<'static, [u8]>>) -> Handle {
|
||||
Handle {
|
||||
symbolic: false,
|
||||
variant: Variant::Svg(svg::Handle::from_memory(bytes)),
|
||||
data: Data::Svg(svg::Handle::from_memory(bytes)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,19 @@
|
|||
|
||||
//! Lazily-generated SVG icon widget for Iced.
|
||||
|
||||
mod builder;
|
||||
pub use builder::Builder;
|
||||
mod named;
|
||||
use std::ffi::OsStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod handle;
|
||||
pub use handle::Handle;
|
||||
pub use named::Named;
|
||||
|
||||
mod handle;
|
||||
pub use handle::{from_path, from_raster_bytes, from_raster_pixels, from_svg_bytes, Data, Handle};
|
||||
|
||||
use crate::{Element, Renderer};
|
||||
use derive_setters::Setters;
|
||||
use iced::widget::{Image, Svg};
|
||||
use iced::{ContentFit, Length};
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Create an [`Icon`] from a pre-existing [`Handle`]
|
||||
pub fn icon(handle: Handle) -> Icon {
|
||||
|
|
@ -28,38 +29,9 @@ pub fn icon(handle: Handle) -> Icon {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create an [`Icon`] from its path.
|
||||
pub fn from_path(path: PathBuf) -> Icon {
|
||||
icon(handle::from_path(path))
|
||||
}
|
||||
|
||||
/// Create an image [`Icon`] from memory.
|
||||
pub fn from_raster_bytes(
|
||||
bytes: impl Into<Cow<'static, [u8]>>
|
||||
+ std::convert::AsRef<[u8]>
|
||||
+ std::marker::Send
|
||||
+ std::marker::Sync
|
||||
+ 'static,
|
||||
) -> Icon {
|
||||
icon(handle::from_raster_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Create an image [`Icon`] from RGBA data, where you must define the width and height.
|
||||
pub fn from_raster_pixels(
|
||||
width: u32,
|
||||
height: u32,
|
||||
pixels: impl Into<Cow<'static, [u8]>>
|
||||
+ std::convert::AsRef<[u8]>
|
||||
+ std::marker::Send
|
||||
+ std::marker::Sync
|
||||
+ 'static,
|
||||
) -> Icon {
|
||||
icon(handle::from_raster_pixels(width, height, pixels))
|
||||
}
|
||||
|
||||
/// Create a SVG [`Icon`] from memory.
|
||||
pub fn from_svg_bytes(bytes: impl Into<Cow<'static, [u8]>>) -> Icon {
|
||||
icon(handle::from_svg_bytes(bytes))
|
||||
/// Create an icon handle from its XDG icon name.
|
||||
pub fn from_name(name: impl Into<Arc<str>>) -> Named {
|
||||
Named::new(name)
|
||||
}
|
||||
|
||||
/// An image which may be an SVG or PNG.
|
||||
|
|
@ -80,19 +52,40 @@ pub struct Icon {
|
|||
impl Icon {
|
||||
#[must_use]
|
||||
fn into_element<Message: 'static>(self) -> Element<'static, Message> {
|
||||
match self.handle.variant {
|
||||
handle::Variant::Image(handle) => Image::new(handle)
|
||||
let from_image = |handle| {
|
||||
Image::new(handle)
|
||||
.width(self.width.unwrap_or(Length::Fixed(f32::from(self.size))))
|
||||
.height(self.height.unwrap_or(Length::Fixed(f32::from(self.size))))
|
||||
.content_fit(self.content_fit)
|
||||
.into(),
|
||||
handle::Variant::Svg(handle) => Svg::<Renderer>::new(handle)
|
||||
.into()
|
||||
};
|
||||
|
||||
let from_svg = |handle| {
|
||||
Svg::<Renderer>::new(handle)
|
||||
.style(self.style.clone())
|
||||
.width(self.width.unwrap_or(Length::Fixed(f32::from(self.size))))
|
||||
.height(self.height.unwrap_or(Length::Fixed(f32::from(self.size))))
|
||||
.content_fit(self.content_fit)
|
||||
.symbolic(self.handle.symbolic)
|
||||
.into(),
|
||||
.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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,38 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{handle, Handle, Icon};
|
||||
use std::path::PathBuf;
|
||||
use super::{Handle, Icon};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
#[must_use]
|
||||
#[derive(derive_setters::Setters)]
|
||||
pub struct Builder<'a> {
|
||||
#[derive(derive_setters::Setters, Clone, Debug, Hash)]
|
||||
pub struct Named {
|
||||
/// Name of icon to locate in an XDG icon path.
|
||||
name: &'a str,
|
||||
pub(super) name: Arc<str>,
|
||||
|
||||
/// Checks for a fallback if the icon was not found.
|
||||
fallback: bool,
|
||||
pub fallback: bool,
|
||||
|
||||
/// Restrict the lookup to a given scale.
|
||||
#[setters(strip_option)]
|
||||
scale: Option<u16>,
|
||||
pub scale: Option<u16>,
|
||||
|
||||
/// Restrict the lookup to a given size.
|
||||
#[setters(strip_option)]
|
||||
size: Option<u16>,
|
||||
pub size: Option<u16>,
|
||||
|
||||
/// Whether the icon is symbolic or not.
|
||||
pub symbolic: bool,
|
||||
|
||||
/// Prioritizes SVG over PNG
|
||||
prefer_svg: bool,
|
||||
pub prefer_svg: bool,
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub const fn new(name: &'a str) -> Self {
|
||||
impl Named {
|
||||
pub fn new(name: impl Into<Arc<str>>) -> Self {
|
||||
let name = name.into();
|
||||
Self {
|
||||
symbolic: name.ends_with("-symbolic"),
|
||||
name,
|
||||
fallback: true,
|
||||
size: None,
|
||||
|
|
@ -37,12 +42,13 @@ impl<'a> Builder<'a> {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn path(mut self) -> Option<PathBuf> {
|
||||
pub fn path(self) -> Option<PathBuf> {
|
||||
let mut name = &*self.name;
|
||||
crate::icon_theme::DEFAULT.with(|theme| {
|
||||
let theme = theme.borrow();
|
||||
|
||||
let locate = || {
|
||||
let mut lookup = freedesktop_icons::lookup(self.name)
|
||||
let mut lookup = freedesktop_icons::lookup(name)
|
||||
.with_theme(theme.as_ref())
|
||||
.with_cache();
|
||||
|
||||
|
|
@ -65,10 +71,8 @@ impl<'a> Builder<'a> {
|
|||
|
||||
// On failure, attempt to locate fallback icon.
|
||||
if result.is_none() && self.fallback {
|
||||
let name = std::mem::take(&mut self.name);
|
||||
|
||||
for name in name.rmatch_indices('-').map(|(pos, _)| &name[..pos]) {
|
||||
self.name = name;
|
||||
for new_name in name.rmatch_indices('-').map(|(pos, _)| &name[..pos]) {
|
||||
name = new_name;
|
||||
result = locate();
|
||||
if result.is_some() {
|
||||
break;
|
||||
|
|
@ -81,11 +85,9 @@ impl<'a> Builder<'a> {
|
|||
}
|
||||
|
||||
pub fn handle(self) -> Handle {
|
||||
if let Some(path) = self.path() {
|
||||
handle::from_path(path)
|
||||
} else {
|
||||
let bytes: &'static [u8] = &[];
|
||||
handle::from_svg_bytes(bytes)
|
||||
Handle {
|
||||
symbolic: self.symbolic,
|
||||
data: super::Data::Name(self),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,14 +103,20 @@ impl<'a> Builder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Builder<'a>> for Handle {
|
||||
fn from(builder: Builder<'a>) -> Self {
|
||||
impl From<Named> for Handle {
|
||||
fn from(builder: Named) -> Self {
|
||||
builder.handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Builder<'a>> for Icon {
|
||||
fn from(builder: Builder<'a>) -> Self {
|
||||
impl From<Named> for Icon {
|
||||
fn from(builder: Named) -> Self {
|
||||
builder.icon()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message: 'static> From<Named> for crate::Element<'a, Message> {
|
||||
fn from(builder: Named) -> Self {
|
||||
builder.icon().into()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue