2020-11-13 00:21:48 +00:00
|
|
|
use ab_glyph::{PxScale, ScaleFont};
|
|
|
|
use bevy_asset::{Assets, Handle, HandleId};
|
2022-09-06 20:03:40 +00:00
|
|
|
use bevy_ecs::component::Component;
|
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
use bevy_ecs::system::Resource;
|
2022-04-25 13:54:46 +00:00
|
|
|
use bevy_math::Vec2;
|
2021-12-14 03:58:23 +00:00
|
|
|
use bevy_render::texture::Image;
|
2020-11-13 00:21:48 +00:00
|
|
|
use bevy_sprite::TextureAtlas;
|
|
|
|
use bevy_utils::HashMap;
|
|
|
|
|
`text_system` split (#7779)
# Objective
`text_system` runs before the UI layout is calculated and the size of
the text node is determined, so it cannot correctly shape the text to
fit the layout, and has no way of determining if the text needs to be
wrapped.
The function `text_constraint` attempts to determine the size of the
node from the local size constraints in the `Style` component. It can't
be made to work, you have to compute the whole layout to get the correct
size. A simple example of where this fails completely is a text node set
to stretch to fill the empty space adjacent to a node with size
constraints set to `Val::Percent(50.)`. The text node will take up half
the space, even though its size constraints are `Val::Auto`
Also because the `text_system` queries for changes to the `Style`
component, when a style value is changed that doesn't affect the node's
geometry the text is recomputed unnecessarily.
Querying on changes to `Node` is not much better. The UI layout is
changed to fit the `CalculatedSize` of the text, so the size of the node
is changed and so the text and UI layout get recalculated multiple times
from a single change to a `Text`.
Also, the `MeasureFunc` doesn't work at all, it doesn't have enough
information to fit the text correctly and makes no attempt.
Fixes #7663, #6717, #5834, #1490,
## Solution
Split the `text_system` into two functions:
* `measure_text_system` which calculates the size constraints for the
text node and runs before `UiSystem::Flex`
* `text_system` which runs after `UiSystem::Flex` and generates the
actual text.
* Fix the `MeasureFunc` calculations.
---
Text wrapping in main:
<img width="961" alt="Capturemain"
src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG">
With this PR:
<img width="961" alt="captured_wrap"
src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG">
## Changelog
* Removed the previous fields from `CalculatedSize`. `CalculatedSize`
now contains a boxed `Measure`.
* Added `measurement` module to `bevy_ui`.
* Added the method `create_text_measure` to `TextPipeline`.
* Added a new system `measure_text_system` that runs before
`UiSystem::Flex` that creates a `MeasureFunc` for the text.
* Rescheduled `text_system` to run after `UiSystem::Flex`.
* Added a trait `Measure`. A `Measure` is used to compute the size of a
UI node when the size of that node is based on its content.
* Added `ImageMeasure` and `TextMeasure` which implement `Measure`.
* Added a new component `UiImageSize` which is used by
`update_image_calculated_size_system` to track image size changes.
* Added a `UiImageSize` component to `ImageBundle`.
## Migration Guide
`ImageBundle` has a new component `UiImageSize` which contains the size
of the image bundle's texture and is updated automatically by
`update_image_calculated_size_system`
---------
Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
|
|
|
use glyph_brush_layout::{FontId, GlyphPositioner, SectionGeometry, SectionText};
|
2020-11-13 00:21:48 +00:00
|
|
|
|
|
|
|
use crate::{
|
2023-01-21 00:17:11 +00:00
|
|
|
error::TextError, glyph_brush::GlyphBrush, scale_value, BreakLineOn, Font, FontAtlasSet,
|
|
|
|
FontAtlasWarning, PositionedGlyph, TextAlignment, TextSection, TextSettings, YAxisOrientation,
|
2020-11-13 00:21:48 +00:00
|
|
|
};
|
|
|
|
|
2022-09-06 20:03:40 +00:00
|
|
|
#[derive(Default, Resource)]
|
|
|
|
pub struct TextPipeline {
|
2020-11-13 00:21:48 +00:00
|
|
|
brush: GlyphBrush,
|
|
|
|
map_font_id: HashMap<HandleId, FontId>,
|
|
|
|
}
|
|
|
|
|
2022-09-06 20:03:40 +00:00
|
|
|
/// Render information for a corresponding [`Text`](crate::Text) component.
|
|
|
|
///
|
|
|
|
/// Contains scaled glyphs and their size. Generated via [`TextPipeline::queue_text`].
|
|
|
|
#[derive(Component, Clone, Default, Debug)]
|
2020-11-13 00:21:48 +00:00
|
|
|
pub struct TextLayoutInfo {
|
|
|
|
pub glyphs: Vec<PositionedGlyph>,
|
2022-04-25 13:54:46 +00:00
|
|
|
pub size: Vec2,
|
2020-11-13 00:21:48 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 20:03:40 +00:00
|
|
|
impl TextPipeline {
|
2021-01-25 01:07:43 +00:00
|
|
|
pub fn get_or_insert_font_id(&mut self, handle: &Handle<Font>, font: &Font) -> FontId {
|
2020-11-13 00:21:48 +00:00
|
|
|
let brush = &mut self.brush;
|
|
|
|
*self
|
|
|
|
.map_font_id
|
2022-10-06 13:33:30 +00:00
|
|
|
.entry(handle.id())
|
2020-11-13 00:21:48 +00:00
|
|
|
.or_insert_with(|| brush.add_font(handle.clone(), font.font.clone()))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
pub fn queue_text(
|
|
|
|
&mut self,
|
|
|
|
fonts: &Assets<Font>,
|
2021-01-25 01:07:43 +00:00
|
|
|
sections: &[TextSection],
|
|
|
|
scale_factor: f64,
|
2020-11-13 00:21:48 +00:00
|
|
|
text_alignment: TextAlignment,
|
2023-04-05 21:25:53 +00:00
|
|
|
linebreak_behavior: BreakLineOn,
|
2022-04-25 13:54:46 +00:00
|
|
|
bounds: Vec2,
|
2020-11-13 00:21:48 +00:00
|
|
|
font_atlas_set_storage: &mut Assets<FontAtlasSet>,
|
|
|
|
texture_atlases: &mut Assets<TextureAtlas>,
|
2021-12-14 03:58:23 +00:00
|
|
|
textures: &mut Assets<Image>,
|
2022-09-19 16:12:12 +00:00
|
|
|
text_settings: &TextSettings,
|
2022-11-25 23:49:25 +00:00
|
|
|
font_atlas_warning: &mut FontAtlasWarning,
|
2022-10-18 13:28:34 +00:00
|
|
|
y_axis_orientation: YAxisOrientation,
|
2022-09-06 20:03:40 +00:00
|
|
|
) -> Result<TextLayoutInfo, TextError> {
|
`text_system` split (#7779)
# Objective
`text_system` runs before the UI layout is calculated and the size of
the text node is determined, so it cannot correctly shape the text to
fit the layout, and has no way of determining if the text needs to be
wrapped.
The function `text_constraint` attempts to determine the size of the
node from the local size constraints in the `Style` component. It can't
be made to work, you have to compute the whole layout to get the correct
size. A simple example of where this fails completely is a text node set
to stretch to fill the empty space adjacent to a node with size
constraints set to `Val::Percent(50.)`. The text node will take up half
the space, even though its size constraints are `Val::Auto`
Also because the `text_system` queries for changes to the `Style`
component, when a style value is changed that doesn't affect the node's
geometry the text is recomputed unnecessarily.
Querying on changes to `Node` is not much better. The UI layout is
changed to fit the `CalculatedSize` of the text, so the size of the node
is changed and so the text and UI layout get recalculated multiple times
from a single change to a `Text`.
Also, the `MeasureFunc` doesn't work at all, it doesn't have enough
information to fit the text correctly and makes no attempt.
Fixes #7663, #6717, #5834, #1490,
## Solution
Split the `text_system` into two functions:
* `measure_text_system` which calculates the size constraints for the
text node and runs before `UiSystem::Flex`
* `text_system` which runs after `UiSystem::Flex` and generates the
actual text.
* Fix the `MeasureFunc` calculations.
---
Text wrapping in main:
<img width="961" alt="Capturemain"
src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG">
With this PR:
<img width="961" alt="captured_wrap"
src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG">
## Changelog
* Removed the previous fields from `CalculatedSize`. `CalculatedSize`
now contains a boxed `Measure`.
* Added `measurement` module to `bevy_ui`.
* Added the method `create_text_measure` to `TextPipeline`.
* Added a new system `measure_text_system` that runs before
`UiSystem::Flex` that creates a `MeasureFunc` for the text.
* Rescheduled `text_system` to run after `UiSystem::Flex`.
* Added a trait `Measure`. A `Measure` is used to compute the size of a
UI node when the size of that node is based on its content.
* Added `ImageMeasure` and `TextMeasure` which implement `Measure`.
* Added a new component `UiImageSize` which is used by
`update_image_calculated_size_system` to track image size changes.
* Added a `UiImageSize` component to `ImageBundle`.
## Migration Guide
`ImageBundle` has a new component `UiImageSize` which contains the size
of the image bundle's texture and is updated automatically by
`update_image_calculated_size_system`
---------
Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
|
|
|
let mut scaled_fonts = Vec::with_capacity(sections.len());
|
2021-01-25 01:07:43 +00:00
|
|
|
let sections = sections
|
|
|
|
.iter()
|
|
|
|
.map(|section| {
|
|
|
|
let font = fonts
|
Enforce type safe usage of Handle::get (#4794)
# Objective
- Sometimes, people might load an asset as one type, then use it with an `Asset`s for a different type.
- See e.g. #4784.
- This is especially likely with the Gltf types, since users may not have a clear conceptual model of what types the assets will be.
- We had an instance of this ourselves, in the `scene_viewer` example
## Solution
- Make `Assets::get` require a type safe handle.
---
## Changelog
### Changed
- `Assets::<T>::get` and `Assets::<T>::get_mut` now require that the passed handles are `Handle<T>`, improving the type safety of handles.
### Added
- `HandleUntyped::typed_weak`, a helper function for creating a weak typed version of an exisitng `HandleUntyped`.
## Migration Guide
`Assets::<T>::get` and `Assets::<T>::get_mut` now require that the passed handles are `Handle<T>`, improving the type safety of handles. If you were previously passing in:
- a `HandleId`, use `&Handle::weak(id)` instead, to create a weak handle. You may have been able to store a type safe `Handle` instead.
- a `HandleUntyped`, use `&handle_untyped.typed_weak()` to create a weak handle of the specified type. This is most likely to be the useful when using [load_folder](https://docs.rs/bevy_asset/latest/bevy_asset/struct.AssetServer.html#method.load_folder)
- a `Handle<U>` of of a different type, consider whether this is the correct handle type to store. If it is (i.e. the same handle id is used for multiple different Asset types) use `Handle::weak(handle.id)` to cast to a different type.
2022-05-30 16:59:44 +00:00
|
|
|
.get(§ion.style.font)
|
2021-01-25 01:07:43 +00:00
|
|
|
.ok_or(TextError::NoSuchFont)?;
|
|
|
|
let font_id = self.get_or_insert_font_id(§ion.style.font, font);
|
|
|
|
let font_size = scale_value(section.style.font_size, scale_factor);
|
|
|
|
|
|
|
|
scaled_fonts.push(ab_glyph::Font::as_scaled(&font.font, font_size));
|
|
|
|
|
|
|
|
let section = SectionText {
|
|
|
|
font_id,
|
|
|
|
scale: PxScale::from(font_size),
|
|
|
|
text: §ion.value,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(section)
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, _>>()?;
|
2020-11-13 00:21:48 +00:00
|
|
|
|
2023-01-21 00:17:11 +00:00
|
|
|
let section_glyphs =
|
|
|
|
self.brush
|
2023-04-05 21:25:53 +00:00
|
|
|
.compute_glyphs(§ions, bounds, text_alignment, linebreak_behavior)?;
|
2020-11-13 00:21:48 +00:00
|
|
|
|
|
|
|
if section_glyphs.is_empty() {
|
2022-09-06 20:03:40 +00:00
|
|
|
return Ok(TextLayoutInfo::default());
|
2020-11-13 00:21:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut min_x: f32 = std::f32::MAX;
|
|
|
|
let mut min_y: f32 = std::f32::MAX;
|
|
|
|
let mut max_x: f32 = std::f32::MIN;
|
|
|
|
let mut max_y: f32 = std::f32::MIN;
|
|
|
|
|
2022-02-13 22:33:55 +00:00
|
|
|
for sg in §ion_glyphs {
|
2021-01-25 01:07:43 +00:00
|
|
|
let scaled_font = scaled_fonts[sg.section_index];
|
|
|
|
let glyph = &sg.glyph;
|
`text_system` split (#7779)
# Objective
`text_system` runs before the UI layout is calculated and the size of
the text node is determined, so it cannot correctly shape the text to
fit the layout, and has no way of determining if the text needs to be
wrapped.
The function `text_constraint` attempts to determine the size of the
node from the local size constraints in the `Style` component. It can't
be made to work, you have to compute the whole layout to get the correct
size. A simple example of where this fails completely is a text node set
to stretch to fill the empty space adjacent to a node with size
constraints set to `Val::Percent(50.)`. The text node will take up half
the space, even though its size constraints are `Val::Auto`
Also because the `text_system` queries for changes to the `Style`
component, when a style value is changed that doesn't affect the node's
geometry the text is recomputed unnecessarily.
Querying on changes to `Node` is not much better. The UI layout is
changed to fit the `CalculatedSize` of the text, so the size of the node
is changed and so the text and UI layout get recalculated multiple times
from a single change to a `Text`.
Also, the `MeasureFunc` doesn't work at all, it doesn't have enough
information to fit the text correctly and makes no attempt.
Fixes #7663, #6717, #5834, #1490,
## Solution
Split the `text_system` into two functions:
* `measure_text_system` which calculates the size constraints for the
text node and runs before `UiSystem::Flex`
* `text_system` which runs after `UiSystem::Flex` and generates the
actual text.
* Fix the `MeasureFunc` calculations.
---
Text wrapping in main:
<img width="961" alt="Capturemain"
src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG">
With this PR:
<img width="961" alt="captured_wrap"
src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG">
## Changelog
* Removed the previous fields from `CalculatedSize`. `CalculatedSize`
now contains a boxed `Measure`.
* Added `measurement` module to `bevy_ui`.
* Added the method `create_text_measure` to `TextPipeline`.
* Added a new system `measure_text_system` that runs before
`UiSystem::Flex` that creates a `MeasureFunc` for the text.
* Rescheduled `text_system` to run after `UiSystem::Flex`.
* Added a trait `Measure`. A `Measure` is used to compute the size of a
UI node when the size of that node is based on its content.
* Added `ImageMeasure` and `TextMeasure` which implement `Measure`.
* Added a new component `UiImageSize` which is used by
`update_image_calculated_size_system` to track image size changes.
* Added a `UiImageSize` component to `ImageBundle`.
## Migration Guide
`ImageBundle` has a new component `UiImageSize` which contains the size
of the image bundle's texture and is updated automatically by
`update_image_calculated_size_system`
---------
Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
|
|
|
// The fonts use a coordinate system increasing upwards so ascent is a positive value
|
|
|
|
// and descent is negative, but Bevy UI uses a downwards increasing coordinate system,
|
|
|
|
// so we have to subtract from the baseline position to get the minimum and maximum values.
|
2020-11-13 00:21:48 +00:00
|
|
|
min_x = min_x.min(glyph.position.x);
|
|
|
|
min_y = min_y.min(glyph.position.y - scaled_font.ascent());
|
|
|
|
max_x = max_x.max(glyph.position.x + scaled_font.h_advance(glyph.id));
|
|
|
|
max_y = max_y.max(glyph.position.y - scaled_font.descent());
|
|
|
|
}
|
|
|
|
|
2022-04-25 13:54:46 +00:00
|
|
|
let size = Vec2::new(max_x - min_x, max_y - min_y);
|
2020-11-13 00:21:48 +00:00
|
|
|
|
|
|
|
let glyphs = self.brush.process_glyphs(
|
|
|
|
section_glyphs,
|
2021-01-25 01:07:43 +00:00
|
|
|
§ions,
|
2020-11-13 00:21:48 +00:00
|
|
|
font_atlas_set_storage,
|
|
|
|
fonts,
|
|
|
|
texture_atlases,
|
|
|
|
textures,
|
2022-09-19 16:12:12 +00:00
|
|
|
text_settings,
|
2022-11-25 23:49:25 +00:00
|
|
|
font_atlas_warning,
|
2022-10-18 13:28:34 +00:00
|
|
|
y_axis_orientation,
|
2020-11-13 00:21:48 +00:00
|
|
|
)?;
|
|
|
|
|
2022-09-06 20:03:40 +00:00
|
|
|
Ok(TextLayoutInfo { glyphs, size })
|
2020-11-13 00:21:48 +00:00
|
|
|
}
|
`text_system` split (#7779)
# Objective
`text_system` runs before the UI layout is calculated and the size of
the text node is determined, so it cannot correctly shape the text to
fit the layout, and has no way of determining if the text needs to be
wrapped.
The function `text_constraint` attempts to determine the size of the
node from the local size constraints in the `Style` component. It can't
be made to work, you have to compute the whole layout to get the correct
size. A simple example of where this fails completely is a text node set
to stretch to fill the empty space adjacent to a node with size
constraints set to `Val::Percent(50.)`. The text node will take up half
the space, even though its size constraints are `Val::Auto`
Also because the `text_system` queries for changes to the `Style`
component, when a style value is changed that doesn't affect the node's
geometry the text is recomputed unnecessarily.
Querying on changes to `Node` is not much better. The UI layout is
changed to fit the `CalculatedSize` of the text, so the size of the node
is changed and so the text and UI layout get recalculated multiple times
from a single change to a `Text`.
Also, the `MeasureFunc` doesn't work at all, it doesn't have enough
information to fit the text correctly and makes no attempt.
Fixes #7663, #6717, #5834, #1490,
## Solution
Split the `text_system` into two functions:
* `measure_text_system` which calculates the size constraints for the
text node and runs before `UiSystem::Flex`
* `text_system` which runs after `UiSystem::Flex` and generates the
actual text.
* Fix the `MeasureFunc` calculations.
---
Text wrapping in main:
<img width="961" alt="Capturemain"
src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG">
With this PR:
<img width="961" alt="captured_wrap"
src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG">
## Changelog
* Removed the previous fields from `CalculatedSize`. `CalculatedSize`
now contains a boxed `Measure`.
* Added `measurement` module to `bevy_ui`.
* Added the method `create_text_measure` to `TextPipeline`.
* Added a new system `measure_text_system` that runs before
`UiSystem::Flex` that creates a `MeasureFunc` for the text.
* Rescheduled `text_system` to run after `UiSystem::Flex`.
* Added a trait `Measure`. A `Measure` is used to compute the size of a
UI node when the size of that node is based on its content.
* Added `ImageMeasure` and `TextMeasure` which implement `Measure`.
* Added a new component `UiImageSize` which is used by
`update_image_calculated_size_system` to track image size changes.
* Added a `UiImageSize` component to `ImageBundle`.
## Migration Guide
`ImageBundle` has a new component `UiImageSize` which contains the size
of the image bundle's texture and is updated automatically by
`update_image_calculated_size_system`
---------
Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
|
|
|
|
|
|
|
pub fn create_text_measure(
|
|
|
|
&mut self,
|
|
|
|
fonts: &Assets<Font>,
|
|
|
|
sections: &[TextSection],
|
|
|
|
scale_factor: f64,
|
|
|
|
text_alignment: TextAlignment,
|
|
|
|
linebreak_behaviour: BreakLineOn,
|
|
|
|
) -> Result<TextMeasureInfo, TextError> {
|
|
|
|
let mut auto_fonts = Vec::with_capacity(sections.len());
|
|
|
|
let mut scaled_fonts = Vec::with_capacity(sections.len());
|
|
|
|
let sections = sections
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, section)| {
|
|
|
|
let font = fonts
|
|
|
|
.get(§ion.style.font)
|
|
|
|
.ok_or(TextError::NoSuchFont)?;
|
|
|
|
let font_size = scale_value(section.style.font_size, scale_factor);
|
|
|
|
auto_fonts.push(font.font.clone());
|
|
|
|
let px_scale_font = ab_glyph::Font::into_scaled(font.font.clone(), font_size);
|
|
|
|
scaled_fonts.push(px_scale_font);
|
|
|
|
|
|
|
|
let section = TextMeasureSection {
|
|
|
|
font_id: FontId(i),
|
|
|
|
scale: PxScale::from(font_size),
|
|
|
|
text: section.value.clone(),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(section)
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
|
|
|
|
Ok(TextMeasureInfo::new(
|
|
|
|
auto_fonts,
|
|
|
|
scaled_fonts,
|
|
|
|
sections,
|
|
|
|
text_alignment,
|
|
|
|
linebreak_behaviour.into(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct TextMeasureSection {
|
|
|
|
pub text: String,
|
|
|
|
pub scale: PxScale,
|
|
|
|
pub font_id: FontId,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct TextMeasureInfo {
|
|
|
|
pub fonts: Vec<ab_glyph::FontArc>,
|
|
|
|
pub scaled_fonts: Vec<ab_glyph::PxScaleFont<ab_glyph::FontArc>>,
|
|
|
|
pub sections: Vec<TextMeasureSection>,
|
|
|
|
pub text_alignment: TextAlignment,
|
|
|
|
pub linebreak_behaviour: glyph_brush_layout::BuiltInLineBreaker,
|
|
|
|
pub min_width_content_size: Vec2,
|
|
|
|
pub max_width_content_size: Vec2,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextMeasureInfo {
|
|
|
|
fn new(
|
|
|
|
fonts: Vec<ab_glyph::FontArc>,
|
|
|
|
scaled_fonts: Vec<ab_glyph::PxScaleFont<ab_glyph::FontArc>>,
|
|
|
|
sections: Vec<TextMeasureSection>,
|
|
|
|
text_alignment: TextAlignment,
|
|
|
|
linebreak_behaviour: glyph_brush_layout::BuiltInLineBreaker,
|
|
|
|
) -> Self {
|
|
|
|
let mut info = Self {
|
|
|
|
fonts,
|
|
|
|
scaled_fonts,
|
|
|
|
sections,
|
|
|
|
text_alignment,
|
|
|
|
linebreak_behaviour,
|
|
|
|
min_width_content_size: Vec2::ZERO,
|
|
|
|
max_width_content_size: Vec2::ZERO,
|
|
|
|
};
|
|
|
|
|
|
|
|
let section_texts = info.prepare_section_texts();
|
|
|
|
let min =
|
|
|
|
info.compute_size_from_section_texts(§ion_texts, Vec2::new(0.0, f32::INFINITY));
|
|
|
|
let max = info.compute_size_from_section_texts(
|
|
|
|
§ion_texts,
|
|
|
|
Vec2::new(f32::INFINITY, f32::INFINITY),
|
|
|
|
);
|
|
|
|
info.min_width_content_size = min;
|
|
|
|
info.max_width_content_size = max;
|
|
|
|
info
|
|
|
|
}
|
|
|
|
|
|
|
|
fn prepare_section_texts(&self) -> Vec<SectionText> {
|
|
|
|
self.sections
|
|
|
|
.iter()
|
|
|
|
.map(|section| SectionText {
|
|
|
|
font_id: section.font_id,
|
|
|
|
scale: section.scale,
|
|
|
|
text: §ion.text,
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compute_size_from_section_texts(&self, sections: &[SectionText], bounds: Vec2) -> Vec2 {
|
|
|
|
let geom = SectionGeometry {
|
|
|
|
bounds: (bounds.x, bounds.y),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
let section_glyphs = glyph_brush_layout::Layout::default()
|
|
|
|
.h_align(self.text_alignment.into())
|
|
|
|
.line_breaker(self.linebreak_behaviour)
|
|
|
|
.calculate_glyphs(&self.fonts, &geom, sections);
|
|
|
|
|
|
|
|
let mut min_x: f32 = std::f32::MAX;
|
|
|
|
let mut min_y: f32 = std::f32::MAX;
|
|
|
|
let mut max_x: f32 = std::f32::MIN;
|
|
|
|
let mut max_y: f32 = std::f32::MIN;
|
|
|
|
|
|
|
|
for sg in section_glyphs {
|
|
|
|
let scaled_font = &self.scaled_fonts[sg.section_index];
|
|
|
|
let glyph = &sg.glyph;
|
|
|
|
// The fonts use a coordinate system increasing upwards so ascent is a positive value
|
|
|
|
// and descent is negative, but Bevy UI uses a downwards increasing coordinate system,
|
|
|
|
// so we have to subtract from the baseline position to get the minimum and maximum values.
|
|
|
|
min_x = min_x.min(glyph.position.x);
|
|
|
|
min_y = min_y.min(glyph.position.y - scaled_font.ascent());
|
|
|
|
max_x = max_x.max(glyph.position.x + scaled_font.h_advance(glyph.id));
|
|
|
|
max_y = max_y.max(glyph.position.y - scaled_font.descent());
|
|
|
|
}
|
|
|
|
|
|
|
|
Vec2::new(max_x - min_x, max_y - min_y)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn compute_size(&self, bounds: Vec2) -> Vec2 {
|
|
|
|
let sections = self.prepare_section_texts();
|
|
|
|
self.compute_size_from_section_texts(§ions, bounds)
|
|
|
|
}
|
2020-11-13 00:21:48 +00:00
|
|
|
}
|