diff --git a/crates/bevy_text/src/font.rs b/crates/bevy_text/src/font.rs index 3c0f804305d42..a66230192b253 100644 --- a/crates/bevy_text/src/font.rs +++ b/crates/bevy_text/src/font.rs @@ -1,16 +1,16 @@ -use crate::ComputedTextBlock; use crate::FontCx; +use crate::FontSource; +use crate::TextFont; use bevy_asset::Asset; use bevy_asset::AssetId; use bevy_asset::Assets; +use bevy_ecs::change_detection::DetectChangesMut; use bevy_ecs::system::Local; use bevy_ecs::system::Query; -use bevy_ecs::system::Res; use bevy_ecs::system::ResMut; use bevy_platform::collections::HashSet; use bevy_reflect::TypePath; use parley::fontique::Blob; -use parley::fontique::FontInfoOverride; use smol_str::SmolStr; /// An [`Asset`] that contains the data for a loaded font, if loaded as an asset. @@ -29,8 +29,9 @@ use smol_str::SmolStr; pub struct Font { /// Content of a font file as bytes pub data: Blob, - /// Font family name. - /// If the font file is a collection with multiple families, the first family name from the last font is used. + /// Font family name used to resolve this asset when referenced by handle. + /// If the font file is a collection with multiple families, this is the family name from the + /// first font face in the collection. pub family_name: SmolStr, } @@ -46,32 +47,76 @@ impl Font { /// Add new font assets to the internal font collection. pub fn load_font_assets_into_font_collection( - fonts: Res>, + mut fonts: ResMut>, mut loaded_fonts: Local>>, mut font_cx: ResMut, - mut text_block_query: Query<&mut ComputedTextBlock>, + mut text_font_query: Query<&mut TextFont>, ) { - let mut new_fonts_added = false; - loaded_fonts.retain(|id| fonts.contains(*id)); - for (id, font) in fonts.iter() { - if loaded_fonts.insert(id) { - font_cx.0.collection.register_fonts( - font.data.clone(), - Some(FontInfoOverride { - family_name: Some(font.family_name.as_str()), - ..Default::default() - }), - ); - new_fonts_added = true; + let new_asset_ids: Vec<_> = fonts.ids().filter(|id| loaded_fonts.insert(*id)).collect(); + + if new_asset_ids.is_empty() { + return; + } + + let mut new_family_ids = Vec::new(); + for asset_id in new_asset_ids.iter() { + let font_data = fonts + .get(*asset_id) + .expect("AssetId should have a corresponding asset") + .data + .clone(); + + let new_fonts = font_cx.collection.register_fonts(font_data, None); + + if let Some((_, family_id)) = new_fonts + .iter() + .flat_map(|(family_id, fonts)| { + fonts + .iter() + .map(move |font_info| (font_info.index(), *family_id)) + }) + .min_by_key(|(index, _)| *index) + && let Some(family_name) = font_cx.0.collection.family_name(family_id) + && let Some(font) = fonts.get_mut_untracked(*asset_id) + { + font.family_name = family_name.into(); + new_family_ids.extend(new_fonts.iter().map(|(family_id, _)| *family_id)); } } - // Whenever new fonts are added, update all text blocks so they use the new fonts. - if new_fonts_added { - for mut block in text_block_query.iter_mut() { - block.needs_rerender = true; + for mut text_font in text_font_query.iter_mut() { + if match &text_font.font { + FontSource::Handle(handle) => new_asset_ids.contains(&handle.id()), + FontSource::Family(name) => font_cx + .collection + .family_id(name) + .is_some_and(|id| new_family_ids.contains(&id)), + generic_source => { + let generic_family = match generic_source { + FontSource::Handle(_) | FontSource::Family(_) => unreachable!(), + FontSource::Serif => parley::GenericFamily::Serif, + FontSource::SansSerif => parley::GenericFamily::SansSerif, + FontSource::Cursive => parley::GenericFamily::Cursive, + FontSource::Fantasy => parley::GenericFamily::Fantasy, + FontSource::Monospace => parley::GenericFamily::Monospace, + FontSource::SystemUi => parley::GenericFamily::SystemUi, + FontSource::UiSerif => parley::GenericFamily::UiSerif, + FontSource::UiSansSerif => parley::GenericFamily::UiSansSerif, + FontSource::UiMonospace => parley::GenericFamily::UiMonospace, + FontSource::UiRounded => parley::GenericFamily::UiRounded, + FontSource::Emoji => parley::GenericFamily::Emoji, + FontSource::Math => parley::GenericFamily::Math, + FontSource::FangSong => parley::GenericFamily::FangSong, + }; + font_cx + .collection + .generic_families(generic_family) + .any(|id| new_family_ids.contains(&id)) + } + } { + text_font.set_changed(); } } } diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 2fe5dfda8995f..6164c4ffc98d4 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -125,8 +125,8 @@ impl Plugin for TextPlugin { .add_systems( PostUpdate, ( - detect_text_needs_rerender, load_font_assets_into_font_collection, + detect_text_needs_rerender, ) .chain(), ) diff --git a/crates/bevy_ui/src/widget/text_input_layout.rs b/crates/bevy_ui/src/widget/text_input_layout.rs index 3764a11b24b79..87165a5ae0add 100644 --- a/crates/bevy_ui/src/widget/text_input_layout.rs +++ b/crates/bevy_ui/src/widget/text_input_layout.rs @@ -82,7 +82,6 @@ pub fn update_editable_text_content_size( || text_font.is_changed() || line_height.is_changed() || target.is_changed() - || fonts.is_changed() || rem_size.is_changed()) { continue; @@ -174,10 +173,8 @@ pub fn update_editable_text_styles( for (mut editable_text, text_font, line_height, target, text_layout) in editable_text_query.iter_mut() { - let editor = editable_text.editor_mut(); - - if f32::EPSILON < (target.scale_factor() - editor.get_scale()).abs() { - editor.set_scale(target.scale_factor()); + if f32::EPSILON < (target.scale_factor() - editable_text.editor.get_scale()).abs() { + editable_text.editor.set_scale(target.scale_factor()); } if text_font.is_changed() @@ -187,9 +184,12 @@ pub fn update_editable_text_styles( FontSize::Vw(_) | FontSize::Vh(_) | FontSize::VMin(_) | FontSize::VMax(_) ) && target.is_changed() { - editor.edit_styles().insert(StyleProperty::FontSize( - text_font.font_size.eval(target.logical_size(), rem_size.0), - )); + editable_text + .editor + .edit_styles() + .insert(StyleProperty::FontSize( + text_font.font_size.eval(target.logical_size(), rem_size.0), + )); } if text_font.is_changed() {