diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f8f6aa01..6f3f244ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ### Features +- **core**: Added the `named_svgs` module to enable sharing SVGs using string keys, replacing the need for `IconTheme`. (#658 @M-Adoo) - **core**: The `keyframes!` macro has been introduced to manage the intermediate steps of animation states. (#653 @M-Adoo) - **core**: Added `QueryId` as a replacement for `TypeId` to facilitate querying types by Provider across different binaries. (#656 @M-Adoo) - **widgets**: Added `LinearProgress` and `SpinnerProgress` widgets along with their respective material themes. (#630 @wjian23 @M-Adoo) diff --git a/core/src/builtin_widgets/theme/miss_icon.svg b/core/src/builtin_widgets/default_named.svg similarity index 100% rename from core/src/builtin_widgets/theme/miss_icon.svg rename to core/src/builtin_widgets/default_named.svg diff --git a/core/src/builtin_widgets/svg.rs b/core/src/builtin_widgets/svg.rs index 78f3c7dbb..04b3162c4 100644 --- a/core/src/builtin_widgets/svg.rs +++ b/core/src/builtin_widgets/svg.rs @@ -9,3 +9,62 @@ impl Render for Svg { painter.draw_svg(self); } } + +pub mod named_svgs { + use std::sync::{LazyLock, Mutex}; + + pub use super::*; + + const DEFAULT_SVG_KEY: &str = "__RIRBIR_DEFAULT_SVG__"; + static SVGS: LazyLock>> = LazyLock::new(|| { + let svg = include_crate_svg!("src/builtin_widgets/default_named.svg"); + let mut set = ahash::AHashMap::new(); + set.insert(DEFAULT_SVG_KEY, svg); + Mutex::new(set) + }); + + /// Register an SVG with a specific name. You can then use the same `name` + /// parameter with [`named_svgs::get`](get) to retrieve it. + /// + /// To prevent conflicts, it is recommended to add a namespace prefix from + /// your library or application to the name, such as `ribir::add`. + pub fn register(name: &'static str, svg: Svg) { SVGS.lock().unwrap().insert(name, svg); } + + /// Retrieve a named SVG that was registered using + /// [`named_svgs::register`](register). + pub fn get(name: &str) -> Option { SVGS.lock().unwrap().get(name).cloned() } + + /// Functions similarly to [`named_svgs::get`](get), but returns the + /// default SVG if not found. + pub fn get_or_default(name: &str) -> Svg { + get(name).unwrap_or_else(|| get(DEFAULT_SVG_KEY).unwrap()) + } +} + +#[cfg(test)] +mod tests { + use ribir_dev_helper::*; + + use super::*; + + fn svgs_smoke() -> Painter { + named_svgs::register( + "test::add", + Svg::parse_from_bytes( + r#""#.as_bytes(), + ).unwrap(), + ); + let mut painter = Painter::new(Rect::from_size(Size::new(128., 64.))); + let add = named_svgs::get("test::add").unwrap(); + let x = named_svgs::get_or_default("x"); + + painter + .draw_svg(&add) + .translate(64., 0.) + .draw_svg(&x); + + painter + } + + painter_backend_eq_image_test!(svgs_smoke, comparison = 0.001); +} diff --git a/core/src/builtin_widgets/theme/icon_theme.rs b/core/src/builtin_widgets/theme/icon_theme.rs index 26e1a076b..722002bec 100644 --- a/core/src/builtin_widgets/theme/icon_theme.rs +++ b/core/src/builtin_widgets/theme/icon_theme.rs @@ -1,3 +1,5 @@ +// todo: replace the icon theme with named_svgs and icon fonts for improved +// functionality. use std::collections::HashMap; use crate::{prelude::*, render_helper::RenderProxy}; @@ -68,7 +70,7 @@ impl Compose for NamedSvg { impl IconTheme { pub fn new(icon_size: IconSize) -> Self { - let svg = include_crate_svg!("src/builtin_widgets/theme/miss_icon.svg"); + let svg = include_crate_svg!("src/builtin_widgets/default_named.svg"); let miss_icon = Resource::new(svg); let mut icons = HashMap::<_, _, ahash::RandomState>::default(); icons.insert(MISS_ICON, miss_icon); diff --git a/test_cases/ribir_core/builtin_widgets/svg/tests/svgs_smoke_wgpu.png b/test_cases/ribir_core/builtin_widgets/svg/tests/svgs_smoke_wgpu.png new file mode 100644 index 000000000..461336091 Binary files /dev/null and b/test_cases/ribir_core/builtin_widgets/svg/tests/svgs_smoke_wgpu.png differ