Skip to content

Commit

Permalink
Improved font handling (#6)
Browse files Browse the repository at this point in the history
* Add system for conveniently loading fonts

* Use the font plugin to only specify a font family, not each font individually

* Simplify querying for a font handle

* Improve structure of font module and exports

* Improve syntax for configuring font folder and make it optional

* Example setup doesn't need the AssetServer anymore
  • Loading branch information
TimJentzsch authored Jul 26, 2024
1 parent f05e5a0 commit 9fb125e
Show file tree
Hide file tree
Showing 14 changed files with 274 additions and 60 deletions.
61 changes: 60 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ categories = ["game-development"]
exclude = ["assets/**/*", ".github/**/*"]

[dependencies]
ab_glyph = "0.2.28"
fontdb = "0.20.0"
nom = "7.1.3"
tinyvec = "1.8.0"

[dependencies.bevy]
version = "0.14"
Expand Down
5 changes: 5 additions & 0 deletions assets/CREDITS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Credits

| Path | License |
| ------- | --------- |
| `fonts` | `OFL.txt` |
File renamed without changes.
9 changes: 3 additions & 6 deletions examples/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,18 @@ struct TimeMarker;

fn main() {
App::new()
.add_plugins((DefaultPlugins, BbcodePlugin))
.add_plugins((DefaultPlugins, BbcodePlugin::new().with_fonts("fonts")))
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());

commands.spawn(BbcodeBundle::from_content(
"Time passed: [m=time]0.0[/m] s",
BbcodeSettings::new(40., Color::WHITE)
.with_regular_font(asset_server.load("fonts/FiraSans-Regular.ttf"))
.with_bold_font(asset_server.load("fonts/FiraSans-Bold.ttf"))
.with_italic_font(asset_server.load("fonts/FiraSans-Italic.ttf"))
BbcodeSettings::new("Fira Sans", 40., Color::WHITE)
// Register the marker component for the `m=time` tag
.with_marker("time", TimeMarker),
));
Expand Down
11 changes: 4 additions & 7 deletions examples/static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@ use bevy_mod_bbcode::{BbcodeBundle, BbcodePlugin, BbcodeSettings};

fn main() {
App::new()
.add_plugins((DefaultPlugins, BbcodePlugin))
.add_plugins((DefaultPlugins, BbcodePlugin::new().with_fonts("fonts")))
.add_systems(Startup, setup)
.run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());

commands.spawn(BbcodeBundle::from_content(
"test [b]bold[/b] with [i]italic[/i] and [c=#ff00ff]color[/c]",
BbcodeSettings::new(40., Color::WHITE)
.with_regular_font(asset_server.load("fonts/FiraSans-Regular.ttf"))
.with_bold_font(asset_server.load("fonts/FiraSans-Bold.ttf"))
.with_italic_font(asset_server.load("fonts/FiraSans-Italic.ttf")),
"test [b]bold with [i]italic[/i][/b] and [c=#ff00ff]color[/c]",
BbcodeSettings::new("Fira Sans", 40., Color::WHITE),
));
}
29 changes: 3 additions & 26 deletions src/bevy/bbcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,23 @@ pub(crate) struct Modifiers {

#[derive(Clone, Component)]
pub struct BbcodeSettings {
pub font_family: String,
pub font_size: f32,
pub color: Color,

pub(crate) regular_font: Option<Handle<Font>>,
pub(crate) bold_font: Option<Handle<Font>>,
pub(crate) italic_font: Option<Handle<Font>>,

pub(crate) modifiers: Modifiers,
}

impl BbcodeSettings {
pub fn new(font_size: f32, color: Color) -> Self {
pub fn new<F: Into<String>>(font_family: F, font_size: f32, color: Color) -> Self {
Self {
font_family: font_family.into(),
font_size,
color,
regular_font: None,
bold_font: None,
italic_font: None,
modifiers: Default::default(),
}
}

/// Add a font to use for regular text.
pub fn with_regular_font(mut self, handle: Handle<Font>) -> Self {
self.regular_font = Some(handle);
self
}

/// Add a font to use for bold text.
pub fn with_bold_font(mut self, handle: Handle<Font>) -> Self {
self.bold_font = Some(handle);
self
}

/// Add a font to use for italic text.
pub fn with_italic_font(mut self, handle: Handle<Font>) -> Self {
self.italic_font = Some(handle);
self
}

/// Register a marker component for the `[m]` tag.
pub fn with_marker<N: Into<String>, M: Component + Clone>(
mut self,
Expand Down
37 changes: 23 additions & 14 deletions src/bevy/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use bevy::{ecs::system::EntityCommands, prelude::*};

use crate::bbcode::{parser::parse_bbcode, BbcodeNode, BbcodeTag};

use super::bbcode::{Bbcode, BbcodeSettings};
use super::{
bbcode::{Bbcode, BbcodeSettings},
font::FontRegistry,
};

#[derive(Debug, Clone)]
struct BbcodeContext {
Expand Down Expand Up @@ -69,9 +72,10 @@ impl BbcodeContext {
pub fn convert_bbcode(
mut commands: Commands,
bbcode_query: Query<(Entity, Ref<Bbcode>, Ref<BbcodeSettings>)>,
font_registry: Res<FontRegistry>,
) {
for (entity, bbcode, settings) in bbcode_query.iter() {
if !bbcode.is_changed() && !settings.is_changed() {
if !bbcode.is_changed() && !settings.is_changed() && !font_registry.is_changed() {
continue;
}

Expand Down Expand Up @@ -100,6 +104,7 @@ pub fn convert_bbcode(
},
&settings,
&nodes,
font_registry.as_ref(),
)
}
}
Expand All @@ -109,23 +114,26 @@ fn construct_recursively(
context: BbcodeContext,
settings: &BbcodeSettings,
nodes: &Vec<Arc<BbcodeNode>>,
font_registry: &FontRegistry,
) {
let default_font = settings.regular_font.clone().unwrap_or_default();

for node in nodes {
match **node {
BbcodeNode::Text(ref text) => {
let font = match (context.is_bold, context.is_italic) {
(true, _) => default_font.clone(),
(_, true) => settings
.italic_font
.clone()
.unwrap_or_else(|| default_font.clone()),
(false, false) => settings
.regular_font
.clone()
.unwrap_or_else(|| default_font.clone()),
let font_query = fontdb::Query {
families: &[fontdb::Family::Name(&settings.font_family)],
weight: if context.is_bold {
fontdb::Weight::BOLD
} else {
fontdb::Weight::NORMAL
},
stretch: fontdb::Stretch::Normal,
style: if context.is_italic {
fontdb::Style::Italic
} else {
fontdb::Style::Normal
},
};
let font = font_registry.query_handle(&font_query).unwrap_or_default();

entity_commands.with_children(|builder| {
let mut text_commands = builder.spawn(TextBundle::from_section(
Expand All @@ -151,6 +159,7 @@ fn construct_recursively(
context.apply_tag(tag),
settings,
tag.children(),
font_registry,
),
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/bevy/font/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod plugin;
mod registry;

pub use plugin::FontPlugin;
pub use registry::FontRegistry;
30 changes: 30 additions & 0 deletions src/bevy/font/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use bevy::prelude::*;

use super::registry::FontRegistry;

#[derive(Debug)]
pub struct FontPlugin;

impl Plugin for FontPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<FontRegistry>()
.add_systems(Update, update_font_registry);
}
}

/// Track when fonts are loaded, modified or removed and update the font registry accordingly.
fn update_font_registry(
mut font_registry: ResMut<FontRegistry>,
mut font_changes: EventReader<AssetEvent<Font>>,
font_assets: Res<Assets<Font>>,
) {
for change in font_changes.read() {
match *change {
AssetEvent::Added { id } => font_registry.add(id, font_assets.as_ref()),
AssetEvent::Modified { id } => font_registry.update(id, font_assets.as_ref()),
AssetEvent::Removed { id } => font_registry.remove(id),
AssetEvent::Unused { id: _ } => {}
AssetEvent::LoadedWithDependencies { id: _ } => {}
}
}
}
Loading

0 comments on commit 9fb125e

Please sign in to comment.