bevy/crates/bevy_text/src/lib.rs
Carter Anderson 513be52505
AnimationEvent -> Event and other improvements (#16440)
# Objective

Needing to derive `AnimationEvent` for `Event` is unnecessary, and the
trigger logic coupled to it feels like we're coupling "event producer"
logic with the event itself, which feels wrong. It also comes with a
bunch of complexity, which is again unnecessary. We can have the
flexibility of "custom animation event trigger logic" without this
coupling and complexity.

The current `animation_events` example is also needlessly complicated,
due to it needing to work around system ordering issues. The docs
describing it are also slightly wrong. We can make this all a non-issue
by solving the underlying ordering problem.

Related to this, we use the `bevy_animation::Animation` system set to
solve PostUpdate animation order-of-operations issues. If we move this
to bevy_app as part of our "core schedule", we can cut out needless
`bevy_animation` crate dependencies in these instances.

## Solution

- Remove `AnimationEvent`, the derive, and all other infrastructure
associated with it (such as the `bevy_animation/derive` crate)
- Replace all instances of `AnimationEvent` traits with `Event + Clone`
- Store and use functions for custom animation trigger logic (ex:
`clip.add_event_fn()`). For "normal" cases users dont need to think
about this and should use the simpler `clip.add_event()`
- Run the `Animation` system set _before_ updating text
- Move `bevy_animation::Animation` to `bevy_app::Animation`. Remove
unnecessary `bevy_animation` dependency from `bevy_ui`
- Adjust `animation_events` example to use the simpler `clip.add_event`
API, as the workarounds are no longer necessary

This is polishing work that will land in 0.15, and I think it is simple
enough and valuable enough to land in 0.15 with it, in the interest of
making the feature as compelling as possible.
2024-11-22 00:16:04 +00:00

162 lines
5.9 KiB
Rust

//! This crate provides the tools for positioning and rendering text in Bevy.
//!
//! # `Font`
//!
//! Fonts contain information for drawing glyphs, which are shapes that typically represent a single character,
//! but in some cases part of a "character" (grapheme clusters) or more than one character (ligatures).
//!
//! A font *face* is part of a font family,
//! and is distinguished by its style (e.g. italic), its weight (e.g. bold) and its stretch (e.g. condensed).
//!
//! In Bevy, [`Font`]s are loaded by the [`FontLoader`] as [assets](bevy_asset::AssetPlugin).
//!
//! # `TextPipeline`
//!
//! The [`TextPipeline`] resource does all of the heavy lifting for rendering text.
//!
//! UI `Text` is first measured by creating a [`TextMeasureInfo`] in [`TextPipeline::create_text_measure`],
//! which is called by the `measure_text_system` system of `bevy_ui`.
//!
//! Note that text measurement is only relevant in a UI context.
//!
//! With the actual text bounds defined, the `bevy_ui::widget::text::text_system` system (in a UI context)
//! or [`text2d::update_text2d_layout`] system (in a 2d world space context)
//! passes it into [`TextPipeline::queue_text`], which:
//!
//! 1. updates a [`Buffer`](cosmic_text::Buffer) from the [`TextSpan`]s, generating new [`FontAtlasSet`]s if necessary.
//! 2. iterates over each glyph in the [`Buffer`](cosmic_text::Buffer) to create a [`PositionedGlyph`],
//! retrieving glyphs from the cache, or rasterizing to a [`FontAtlas`] if necessary.
//! 3. [`PositionedGlyph`]s are stored in a [`TextLayoutInfo`],
//! which contains all the information that downstream systems need for rendering.
#![allow(clippy::type_complexity)]
extern crate alloc;
mod bounds;
mod error;
mod font;
mod font_atlas;
mod font_atlas_set;
mod font_loader;
mod glyph;
mod pipeline;
mod text;
mod text2d;
mod text_access;
pub use bounds::*;
pub use error::*;
pub use font::*;
pub use font_atlas::*;
pub use font_atlas_set::*;
pub use font_loader::*;
pub use glyph::*;
pub use pipeline::*;
pub use text::*;
pub use text2d::*;
pub use text_access::*;
/// The text prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[doc(hidden)]
#[allow(deprecated)]
pub use crate::Text2dBundle;
#[doc(hidden)]
pub use crate::{
Font, JustifyText, LineBreak, Text2d, Text2dReader, Text2dWriter, TextColor, TextError,
TextFont, TextLayout, TextSpan,
};
}
use bevy_app::{prelude::*, Animation};
use bevy_asset::AssetApp;
#[cfg(feature = "default_font")]
use bevy_asset::{load_internal_binary_asset, Handle};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::CameraUpdateSystem, view::VisibilitySystems, ExtractSchedule, RenderApp,
};
use bevy_sprite::SpriteSystem;
/// The raw data for the default font used by `bevy_text`
#[cfg(feature = "default_font")]
pub const DEFAULT_FONT_DATA: &[u8] = include_bytes!("FiraMono-subset.ttf");
/// Adds text rendering support to an app.
///
/// When the `bevy_text` feature is enabled with the `bevy` crate, this
/// plugin is included by default in the `DefaultPlugins`.
#[derive(Default)]
pub struct TextPlugin;
/// Text is rendered for two different view projections;
/// 2-dimensional text ([`Text2d`]) is rendered in "world space" with a `BottomToTop` Y-axis,
/// while UI is rendered with a `TopToBottom` Y-axis.
/// This matters for text because the glyph positioning is different in either layout.
/// For `TopToBottom`, 0 is the top of the text, while for `BottomToTop` 0 is the bottom.
pub enum YAxisOrientation {
/// Top to bottom Y-axis orientation, for UI
TopToBottom,
/// Bottom to top Y-axis orientation, for 2d world space
BottomToTop,
}
/// System set in [`PostUpdate`] where all 2d text update systems are executed.
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub struct Update2dText;
impl Plugin for TextPlugin {
fn build(&self, app: &mut App) {
app.init_asset::<Font>()
.register_type::<Text2d>()
.register_type::<TextFont>()
.register_type::<TextColor>()
.register_type::<TextSpan>()
.register_type::<TextBounds>()
.register_type::<TextLayout>()
.register_type::<ComputedTextBlock>()
.init_asset_loader::<FontLoader>()
.init_resource::<FontAtlasSets>()
.init_resource::<TextPipeline>()
.init_resource::<CosmicFontSystem>()
.init_resource::<SwashCache>()
.init_resource::<TextIterScratch>()
.add_systems(
PostUpdate,
(
remove_dropped_font_atlas_sets,
detect_text_needs_rerender::<Text2d>,
update_text2d_layout
// Potential conflict: `Assets<Image>`
// In practice, they run independently since `bevy_render::camera_update_system`
// will only ever observe its own render target, and `update_text2d_layout`
// will never modify a pre-existing `Image` asset.
.ambiguous_with(CameraUpdateSystem),
calculate_bounds_text2d.in_set(VisibilitySystems::CalculateBounds),
)
.chain()
.in_set(Update2dText)
.after(Animation),
)
.add_systems(Last, trim_cosmic_cache);
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(
ExtractSchedule,
extract_text2d_sprite.after(SpriteSystem::ExtractSprites),
);
}
#[cfg(feature = "default_font")]
load_internal_binary_asset!(
app,
Handle::default(),
"FiraMono-subset.ttf",
|bytes: &[u8], _path: String| { Font::try_from_bytes(bytes.to_vec()).unwrap() }
);
}
}