NoWrap Text feature (#8947)
# Objective In Bevy 10.1 and before, the only way to enable text wrapping was to set a local `Val::Px` width constraint on the text node itself. `Val::Percent` constraints and constraints on the text node's ancestors did nothing. #7779 fixed those problems. But perversely displaying unwrapped text is really difficult now, and requires users to nest each `TextBundle` in a `NodeBundle` and apply `min_width` and `max_width` constraints. Some constructions may even need more than one layer of nesting. I've seen several people already who have really struggled with this when porting their projects to main in advance of 0.11. ## Solution Add a `NoWrap` variant to the `BreakLineOn` enum. If `NoWrap` is set, ignore any constraints on the width for the text and call `TextPipeline::queue_text` with a width bound of `f32::INFINITY`. --- ## Changelog * Added a `NoWrap` variant to the `BreakLineOn` enum. * If `NoWrap` is set, any constraints on the width for the text are ignored and `TextPipeline::queue_text` is called with a width bound of `f32::INFINITY`. * Changed the `size` field of `FixedMeasure` to `pub`. This shouldn't have been private, it was always intended to have `pub` visibility. * Added a `with_no_wrap` method to `TextBundle`. ## Migration Guide `bevy_text::text::BreakLineOn` has a new variant `NoWrap` that disables text wrapping for the `Text`. Text wrapping can also be disabled using the `with_no_wrap` method of `TextBundle`.
This commit is contained in:
parent
4b1a502a49
commit
aeea4b0344
@ -106,6 +106,13 @@ impl Text {
|
||||
self.alignment = alignment;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns this [`Text`] with soft wrapping disabled.
|
||||
/// Hard wrapping, where text contains an explicit linebreak such as the escape sequence `\n`, will still occur.
|
||||
pub const fn with_no_wrap(mut self) -> Self {
|
||||
self.linebreak_behavior = BreakLineOn::NoWrap;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, FromReflect, Reflect)]
|
||||
@ -186,12 +193,19 @@ pub enum BreakLineOn {
|
||||
/// This is closer to the behavior one might expect from text in a terminal.
|
||||
/// However it may lead to words being broken up across linebreaks.
|
||||
AnyCharacter,
|
||||
/// No soft wrapping, where text is automatically broken up into separate lines when it overflows a boundary, will ever occur.
|
||||
/// Hard wrapping, where text contains an explicit linebreak such as the escape sequence `\n`, is still enabled.
|
||||
NoWrap,
|
||||
}
|
||||
|
||||
impl From<BreakLineOn> for glyph_brush_layout::BuiltInLineBreaker {
|
||||
fn from(val: BreakLineOn) -> Self {
|
||||
match val {
|
||||
BreakLineOn::WordBoundary => glyph_brush_layout::BuiltInLineBreaker::UnicodeLineBreaker,
|
||||
// If `NoWrap` is set the choice of `BuiltInLineBreaker` doesn't matter as the text is given unbounded width and soft wrapping will never occur.
|
||||
// But `NoWrap` does not disable hard breaks where a [`Text`] contains a newline character.
|
||||
BreakLineOn::WordBoundary | BreakLineOn::NoWrap => {
|
||||
glyph_brush_layout::BuiltInLineBreaker::UnicodeLineBreaker
|
||||
}
|
||||
BreakLineOn::AnyCharacter => glyph_brush_layout::BuiltInLineBreaker::AnyCharLineBreaker,
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,8 +23,8 @@ use bevy_utils::HashSet;
|
||||
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
|
||||
|
||||
use crate::{
|
||||
Font, FontAtlasSet, FontAtlasWarning, PositionedGlyph, Text, TextError, TextLayoutInfo,
|
||||
TextPipeline, TextSettings, YAxisOrientation,
|
||||
BreakLineOn, Font, FontAtlasSet, FontAtlasWarning, PositionedGlyph, Text, TextError,
|
||||
TextLayoutInfo, TextPipeline, TextSettings, YAxisOrientation,
|
||||
};
|
||||
|
||||
/// The maximum width and height of text. The text will wrap according to the specified size.
|
||||
@ -174,7 +174,11 @@ pub fn update_text2d_layout(
|
||||
for (entity, text, bounds, mut text_layout_info) in &mut text_query {
|
||||
if factor_changed || text.is_changed() || bounds.is_changed() || queue.remove(&entity) {
|
||||
let text_bounds = Vec2::new(
|
||||
scale_value(bounds.size.x, scale_factor),
|
||||
if text.linebreak_behavior == BreakLineOn::NoWrap {
|
||||
f32::INFINITY
|
||||
} else {
|
||||
scale_value(bounds.size.x, scale_factor)
|
||||
},
|
||||
scale_value(bounds.size.y, scale_factor),
|
||||
);
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ pub trait Measure: Send + Sync + 'static {
|
||||
/// always returns the same size.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct FixedMeasure {
|
||||
size: Vec2,
|
||||
pub size: Vec2,
|
||||
}
|
||||
|
||||
impl Measure for FixedMeasure {
|
||||
|
||||
@ -13,7 +13,7 @@ use bevy_render::{
|
||||
};
|
||||
use bevy_sprite::TextureAtlas;
|
||||
#[cfg(feature = "bevy_text")]
|
||||
use bevy_text::{Text, TextAlignment, TextLayoutInfo, TextSection, TextStyle};
|
||||
use bevy_text::{BreakLineOn, Text, TextAlignment, TextLayoutInfo, TextSection, TextStyle};
|
||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
|
||||
/// The basic UI node
|
||||
@ -256,6 +256,13 @@ impl TextBundle {
|
||||
self.background_color = BackgroundColor(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns this [`TextBundle`] with soft wrapping disabled.
|
||||
/// Hard wrapping, where text contains an explicit linebreak such as the escape sequence `\n`, will still occur.
|
||||
pub const fn with_no_wrap(mut self) -> Self {
|
||||
self.text.linebreak_behavior = BreakLineOn::NoWrap;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A UI node that is a button
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::{ContentSize, Measure, Node, UiScale};
|
||||
use crate::{ContentSize, FixedMeasure, Measure, Node, UiScale};
|
||||
use bevy_asset::Assets;
|
||||
use bevy_ecs::{
|
||||
prelude::{Component, DetectChanges},
|
||||
@ -12,8 +12,8 @@ use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect, ReflectFrom
|
||||
use bevy_render::texture::Image;
|
||||
use bevy_sprite::TextureAtlas;
|
||||
use bevy_text::{
|
||||
Font, FontAtlasSet, FontAtlasWarning, Text, TextError, TextLayoutInfo, TextMeasureInfo,
|
||||
TextPipeline, TextSettings, YAxisOrientation,
|
||||
BreakLineOn, Font, FontAtlasSet, FontAtlasWarning, Text, TextError, TextLayoutInfo,
|
||||
TextMeasureInfo, TextPipeline, TextSettings, YAxisOrientation,
|
||||
};
|
||||
use bevy_window::{PrimaryWindow, Window};
|
||||
use taffy::style::AvailableSpace;
|
||||
@ -91,7 +91,13 @@ fn create_text_measure(
|
||||
text.linebreak_behavior,
|
||||
) {
|
||||
Ok(measure) => {
|
||||
content_size.set(TextMeasure { info: measure });
|
||||
if text.linebreak_behavior == BreakLineOn::NoWrap {
|
||||
content_size.set(FixedMeasure {
|
||||
size: measure.max_width_content_size,
|
||||
});
|
||||
} else {
|
||||
content_size.set(TextMeasure { info: measure });
|
||||
}
|
||||
|
||||
// Text measure func created succesfully, so set `TextFlags` to schedule a recompute
|
||||
text_flags.needs_new_measure_func = false;
|
||||
@ -174,7 +180,12 @@ fn queue_text(
|
||||
) {
|
||||
// Skip the text node if it is waiting for a new measure func
|
||||
if !text_flags.needs_new_measure_func {
|
||||
let physical_node_size = node.physical_size(scale_factor);
|
||||
let physical_node_size = if text.linebreak_behavior == BreakLineOn::NoWrap {
|
||||
// With `NoWrap` set, no constraints are placed on the width of the text.
|
||||
Vec2::splat(f32::INFINITY)
|
||||
} else {
|
||||
node.physical_size(scale_factor)
|
||||
};
|
||||
|
||||
match text_pipeline.queue_text(
|
||||
fonts,
|
||||
|
||||
@ -33,7 +33,11 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
})
|
||||
.id();
|
||||
|
||||
for linebreak_behavior in [BreakLineOn::AnyCharacter, BreakLineOn::WordBoundary] {
|
||||
for linebreak_behavior in [
|
||||
BreakLineOn::AnyCharacter,
|
||||
BreakLineOn::WordBoundary,
|
||||
BreakLineOn::NoWrap,
|
||||
] {
|
||||
let row_id = commands
|
||||
.spawn(NodeBundle {
|
||||
style: Style {
|
||||
@ -66,7 +70,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
flex_direction: FlexDirection::Column,
|
||||
width: Val::Percent(16.),
|
||||
height: Val::Percent(95.),
|
||||
overflow: Overflow::clip(),
|
||||
overflow: Overflow::clip_x(),
|
||||
..Default::default()
|
||||
},
|
||||
background_color: Color::rgb(0.5, c, 1.0 - c).into(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user