forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
text.rs
148 lines (138 loc) · 5.07 KB
/
text.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use crate::{CalculatedSize, Size, Style, UiScale, Val};
use bevy_asset::Assets;
use bevy_ecs::{
entity::Entity,
query::{Changed, Or, With},
system::{Commands, Local, ParamSet, Query, Res, ResMut},
};
use bevy_math::Vec2;
use bevy_render::texture::Image;
use bevy_sprite::TextureAtlas;
use bevy_text::{Font, FontAtlasSet, Text, TextError, TextLayoutInfo, TextPipeline, TextSettings};
use bevy_window::Windows;
#[derive(Debug, Default)]
pub struct QueuedText {
entities: Vec<Entity>,
}
fn scale_value(value: f32, factor: f64) -> f32 {
(value as f64 * factor) as f32
}
/// Defines how `min_size`, `size`, and `max_size` affects the bounds of a text
/// block.
pub fn text_constraint(min_size: Val, size: Val, max_size: Val, scale_factor: f64) -> f32 {
// Needs support for percentages
match (min_size, size, max_size) {
(_, _, Val::Px(max)) => scale_value(max, scale_factor),
(Val::Px(min), _, _) => scale_value(min, scale_factor),
(Val::Undefined, Val::Px(size), Val::Undefined) | (Val::Auto, Val::Px(size), Val::Auto) => {
scale_value(size, scale_factor)
}
_ => f32::MAX,
}
}
/// Updates the layout and size information whenever the text or style is changed.
/// This information is computed by the `TextPipeline` on insertion, then stored.
#[allow(clippy::too_many_arguments)]
pub fn text_system(
mut commands: Commands,
mut queued_text: Local<QueuedText>,
mut last_scale_factor: Local<f64>,
mut textures: ResMut<Assets<Image>>,
fonts: Res<Assets<Font>>,
windows: Res<Windows>,
text_settings: Res<TextSettings>,
ui_scale: Res<UiScale>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<TextPipeline>,
mut text_queries: ParamSet<(
Query<Entity, Or<(Changed<Text>, Changed<Style>)>>,
Query<Entity, (With<Text>, With<Style>)>,
Query<(
&Text,
&Style,
&mut CalculatedSize,
Option<&mut TextLayoutInfo>,
)>,
)>,
) {
// TODO: This should support window-independent scale settings.
// See https://github.com/bevyengine/bevy/issues/5621
let scale_factor = if let Some(window) = windows.get_primary() {
window.scale_factor() * ui_scale.scale
} else {
ui_scale.scale
};
let inv_scale_factor = 1. / scale_factor;
#[allow(clippy::float_cmp)]
if *last_scale_factor == scale_factor {
// Adds all entities where the text or the style has changed to the local queue
for entity in text_queries.p0().iter() {
queued_text.entities.push(entity);
}
} else {
// If the scale factor has changed, queue all text
for entity in text_queries.p1().iter() {
queued_text.entities.push(entity);
}
*last_scale_factor = scale_factor;
}
if queued_text.entities.is_empty() {
return;
}
// Computes all text in the local queue
let mut new_queue = Vec::new();
let mut query = text_queries.p2();
for entity in queued_text.entities.drain(..) {
if let Ok((text, style, mut calculated_size, text_layout_info)) = query.get_mut(entity) {
let node_size = Vec2::new(
text_constraint(
style.min_size.width,
style.size.width,
style.max_size.width,
scale_factor,
),
text_constraint(
style.min_size.height,
style.size.height,
style.max_size.height,
scale_factor,
),
);
match text_pipeline.queue_text(
&fonts,
&text.sections,
scale_factor,
text.alignment,
node_size,
&mut *font_atlas_set_storage,
&mut *texture_atlases,
&mut *textures,
text_settings.as_ref(),
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, let's add this entity to the
// queue for further processing
new_queue.push(entity);
}
Err(e @ TextError::FailedToAddGlyph(_))
| Err(e @ TextError::ExceedMaxTextAtlases(_)) => {
panic!("Fatal error when processing text: {}.", e);
}
Ok(info) => {
calculated_size.size = Size {
width: Val::Px(scale_value(info.size.x, inv_scale_factor)),
height: Val::Px(scale_value(info.size.y, inv_scale_factor)),
};
match text_layout_info {
Some(mut t) => *t = info,
None => {
commands.entity(entity).insert(info);
}
}
}
}
}
}
queued_text.entities = new_queue;
}