From 08cc955915e392763d1bff8c1a393e0fae9f2e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Maita?= <47983254+mnmaita@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:34:54 +0200 Subject: [PATCH] 0.4.0 (#4) # **BREAKING CHANGE** It is no longer necessary to prepend the main folder where the assets are located when using `FontAssetList`. If you were doing ```rust app.insert_resource(FontAssetList::new( [ "fonts/bold.ttf", "fonts/italic.ttf", "fonts/thin.ttf", ] .into(), )); ``` change this to ```rust app.insert_resource(FontAssetList::new( [ "bold.ttf", "italic.ttf", "thin.ttf", ] .into(), )); ``` The folder will be taken from the `FontAssetFolder` Resource. # What was done - Fixed issues that caused the plugin to hang forever in a `NotLoaded` state. - Refactored the plugin code. - Updated README and added documentation for public types. --- Cargo.toml | 2 +- README.md | 32 +++++++--- src/plugin.rs | 165 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 126 insertions(+), 73 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 825fa04..bcf1f1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trve_bevy_font" -version = "0.3.0" +version = "0.4.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 850e4bf..99e16f3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ trve_bevy_font = { git = "https://github.com/mnmaita/trve_bevy_font" } Remember you can also target tags, commits and branches with this method: ```toml -trve_bevy_font = { git = "https://github.com/mnmaita/trve_bevy_font", tag = "v0.3.0" } +trve_bevy_font = { git = "https://github.com/mnmaita/trve_bevy_font", tag = "v0.4.0" } ``` ```toml @@ -45,25 +45,41 @@ This will load all assets from `assets/ttfs` by using `AssetServer`'s `load_fold ### Loading a list of assets -Certain platforms, like web, can't use `load_folder` to load assets so this library provides an override via the `FontAssetList` Resource. This allows you to load a list of assets from your `assets` folder. +Certain platforms, like web, can't use `load_folder` to load assets so this library provides an override via the `FontAssetList` Resource. -```rs +This allows you to load a list of assets from the folder specified in the `FontAssetFolder` Resource, within the `assets` directory. + +```rust + // This will attempt to load `assets/fonts/bold.ttf`, `assets/fonts/italic.ttf` and `assets/fonts/thin.ttf`. + app.insert_resource(FontAssetList::new( + [ + "bold.ttf", + "italic.ttf", + "thin.ttf", + ] + .into(), + )); +``` + +```rust + // This will attempt to load `assets/ttfs/bold.ttf`, `assets/ttfs/italic.ttf` and `assets/ttfs/thin.ttf`. + app.insert_resource(FontAssetFolder::new("ttfs")); app.insert_resource(FontAssetList::new( [ - "typography/bold.ttf", - "typography/italic.ttf", - "typography/thin.ttf", + "bold.ttf", + "italic.ttf", + "thin.ttf", ] .into(), )); ``` -If you insert this Resource, `FontAssetFolder` will be ignored and the plugin will only load assets based on the provided list. +If you insert this Resource the plugin will **only** load the assets provided in the list. ## Bevy version compatibility | trve_bevy_font | bevy | | -------------- | ---- | -| 0.3 | 0.14 | +| 0.3 0.4 | 0.14 | | 0.2 | 0.13 | | 0.1 | 0.12 | diff --git a/src/plugin.rs b/src/plugin.rs index e84d98b..f6f1223 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -9,16 +9,24 @@ pub struct TrveFontPlugin; impl Plugin for TrveFontPlugin { fn build(&self, app: &mut App) { - app.init_resource::(); app.init_resource::(); - app.add_systems(Startup, load_fonts); + app.add_systems( + Startup, + (setup_resources, load_fonts.after(setup_resources)), + ); app.add_systems( Update, - update_font_assets_load_state.run_if(not(resource_equals(FontLoadState::Loaded))), + update_font_assets_load_state.run_if(not(resource_equals(FontLoadState::LOADED))), ); } } +/// Determines the name of the directory (within the `assets` directory) from where fonts will be loaded. +/// +/// By default, this is set to "fonts". +/// +/// Since `AssetServer::load_folder()` is unsupported in web builds, it will only be used as the base +/// directory for the file names in the `FontAssetList` Resource. #[derive(Resource)] pub struct FontAssetFolder<'a>(AssetPath<'a>); @@ -28,43 +36,60 @@ impl<'a> FontAssetFolder<'a> { } } +impl Default for FontAssetFolder<'_> { + fn default() -> Self { + Self(FONT_ASSET_FOLDER.into()) + } +} + +impl std::fmt::Display for FontAssetFolder<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +/// List of assets to be loaded from the directory specified in the `FontAssetFolder` Resource. +/// +/// Should be a list of file names with their extension. +/// +/// This works as an override for `FontAssetFolder` in non-web platforms so, if set, +/// assets will be loaded individually and only from this list. +/// +/// In web builds this is the default and the only supported option. +/// +/// Example: +/// +/// ``` +/// app.insert_resource(FontAssetList::new( +/// [ +/// "font1.ttf", +/// "font2.ttf", +/// "font3.ttf", +/// ] +/// .to_vec(), +/// )); +/// ``` #[derive(Resource, Default, Deref)] pub struct FontAssetList<'a>(Vec>); impl<'a> FontAssetList<'a> { - pub fn new(path: Vec>>) -> Self { - Self( - path.into_iter() - .map(|path| path.into()) - .collect::>>(), - ) + pub fn new(paths: Vec>>) -> Self { + let asset_paths: Vec> = paths.into_iter().map(|path| path.into()).collect(); + Self(asset_paths) } } -impl Default for FontAssetFolder<'_> { +#[derive(Resource, PartialEq, Deref)] +struct FontLoadState(RecursiveDependencyLoadState); + +impl Default for FontLoadState { fn default() -> Self { - Self(FONT_ASSET_FOLDER.into()) + Self(RecursiveDependencyLoadState::NotLoaded) } } -#[derive(Default, Resource, PartialEq)] -enum FontLoadState { - #[default] - NotLoaded, - Loading, - Loaded, - Failed, -} - -impl From for FontLoadState { - fn from(value: RecursiveDependencyLoadState) -> Self { - match value { - RecursiveDependencyLoadState::NotLoaded => Self::NotLoaded, - RecursiveDependencyLoadState::Loading => Self::Loading, - RecursiveDependencyLoadState::Loaded => Self::Loaded, - RecursiveDependencyLoadState::Failed => Self::Failed, - } - } +impl FontLoadState { + const LOADED: Self = Self(RecursiveDependencyLoadState::Loaded); } #[derive(Resource, Default, Deref, DerefMut)] @@ -73,34 +98,37 @@ struct FontHandles(Vec>); #[derive(Resource, Default, Deref, DerefMut)] struct FontFolderHandle(Handle); +fn setup_resources(mut commands: Commands) { + commands.init_resource::(); + + if cfg!(target_family = "wasm") { + commands.init_resource::(); + } +} + fn load_fonts( mut commands: Commands, asset_server: Res, font_folder: Res>, font_asset_list: Option>>, ) { - if cfg!(not(target_family = "wasm")) && font_asset_list.is_none() { - commands.insert_resource(FontFolderHandle( - asset_server.load_folder(font_folder.0.clone()), - )); - return; + if cfg!(not(target_family = "wasm")) { + if font_asset_list.is_none() { + // TODO: Verify that files in the directory are actually Font handles + commands.insert_resource(FontFolderHandle( + asset_server.load_folder(font_folder.0.clone()), + )); + return; + } } if let Some(font_asset_list) = font_asset_list { - if font_asset_list.is_empty() { - if cfg!(target_family = "wasm") { - info!("FontAssetList Resource is empty."); - } - } else { - commands.insert_resource(FontHandles( - font_asset_list - .iter() - .map(|path| asset_server.load::(path)) - .collect::>>(), - )); - } - } else if cfg!(target_family = "wasm") { - warn!("FontAssetList Resource does not exist."); + let load_font_asset = |path| asset_server.load::(format!("{}/{path}", *font_folder)); + let handles: Vec> = match font_asset_list.is_empty() { + true => Vec::default(), + false => font_asset_list.iter().map(load_font_asset).collect(), + }; + commands.insert_resource(FontHandles(handles)); } } @@ -111,25 +139,34 @@ fn update_font_assets_load_state( font_folder_handle: Option>, font_asset_list: Option>>, ) { - if font_asset_list.is_some() { - if let Some(font_handles) = font_handles { - let all_loaded = font_handles.iter().all(|handle| { - asset_server.recursive_dependency_load_state(handle.id()) - == RecursiveDependencyLoadState::Loaded - }); - *font_load_state = if all_loaded { - RecursiveDependencyLoadState::Loaded.into() - } else { - RecursiveDependencyLoadState::NotLoaded.into() - } + if cfg!(not(target_family = "wasm")) { + if font_asset_list.is_none() { + font_load_state.0 = + asset_server.recursive_dependency_load_state(&font_folder_handle.unwrap().0); + return; } - } else if let Some(font_folder_handle) = font_folder_handle { - *font_load_state = asset_server - .recursive_dependency_load_state(&font_folder_handle.0) - .into() + } + + if let Some(font_handles) = font_handles { + let all_loaded = font_handles.iter().all(|handle| { + if RecursiveDependencyLoadState::Failed + == asset_server.recursive_dependency_load_state(handle) + { + if let Some(path) = handle.path() { + info!("Asset '{path}' failed to load. Make sure the file name is correct and is a font."); + } + return true; + } + asset_server.is_loaded_with_dependencies(handle) + }); + + font_load_state.0 = match all_loaded { + true => RecursiveDependencyLoadState::Loaded, + false => RecursiveDependencyLoadState::NotLoaded, + }; } } pub fn font_assets_loaded() -> impl Condition<()> { - IntoSystem::into_system(resource_equals(FontLoadState::Loaded)) + IntoSystem::into_system(resource_equals(FontLoadState::LOADED)) }