# Objective
- A `Trigger` has multiple associated `Entity`s - the entity observing
the event, and the entity that was targeted by the event.
- The field `entity: Entity` encodes no semantic information about what
the entity is used for, you can already tell that it's an `Entity` by
the type signature!
## Solution
- Rename `trigger.entity()` to `trigger.target()`
---
## Changelog
- `Trigger`s are associated with multiple entities. `Trigger::entity()`
has been renamed to `Trigger::target()` to reflect the semantics of the
entity being returned.
## Migration Guide
- Rename `Trigger::entity()` to `Trigger::target()`.
- Rename `ObserverTrigger::entity` to `ObserverTrigger::target`
# Objective
- Fixes#16208
## Solution
- Added an associated type to `Component`, `Mutability`, which flags
whether a component is mutable, or immutable. If `Mutability= Mutable`,
the component is mutable. If `Mutability= Immutable`, the component is
immutable.
- Updated `derive_component` to default to mutable unless an
`#[component(immutable)]` attribute is added.
- Updated `ReflectComponent` to check if a component is mutable and, if
not, panic when attempting to mutate.
## Testing
- CI
- `immutable_components` example.
---
## Showcase
Users can now mark a component as `#[component(immutable)]` to prevent
safe mutation of a component while it is attached to an entity:
```rust
#[derive(Component)]
#[component(immutable)]
struct Foo {
// ...
}
```
This prevents creating an exclusive reference to the component while it
is attached to an entity. This is particularly powerful when combined
with component hooks, as you can now fully track a component's value,
ensuring whatever invariants you desire are upheld. Before this would be
done my making a component private, and manually creating a `QueryData`
implementation which only permitted read access.
<details>
<summary>Using immutable components as an index</summary>
```rust
/// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)]
#[component(
immutable,
on_insert = on_insert_name,
on_replace = on_replace_name,
)]
pub struct Name(pub &'static str);
/// This index allows for O(1) lookups of an [`Entity`] by its [`Name`].
#[derive(Resource, Default)]
struct NameIndex {
name_to_entity: HashMap<Name, Entity>,
}
impl NameIndex {
fn get_entity(&self, name: &'static str) -> Option<Entity> {
self.name_to_entity.get(&Name(name)).copied()
}
}
fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) {
let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!()
};
let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
return;
};
index.name_to_entity.insert(name, entity);
}
fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) {
let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!()
};
let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
return;
};
index.name_to_entity.remove(&name);
}
// Setup our name index
world.init_resource::<NameIndex>();
// Spawn some entities!
let alyssa = world.spawn(Name("Alyssa")).id();
let javier = world.spawn(Name("Javier")).id();
// Check our index
let index = world.resource::<NameIndex>();
assert_eq!(index.get_entity("Alyssa"), Some(alyssa));
assert_eq!(index.get_entity("Javier"), Some(javier));
// Changing the name of an entity is also fully capture by our index
world.entity_mut(javier).insert(Name("Steven"));
// Javier changed their name to Steven
let steven = javier;
// Check our index
let index = world.resource::<NameIndex>();
assert_eq!(index.get_entity("Javier"), None);
assert_eq!(index.get_entity("Steven"), Some(steven));
```
</details>
Additionally, users can use `Component<Mutability = ...>` in trait
bounds to enforce that a component _is_ mutable or _is_ immutable. When
using `Component` as a trait bound without specifying `Mutability`, any
component is applicable. However, methods which only work on mutable or
immutable components are unavailable, since the compiler must be
pessimistic about the type.
## Migration Guide
- When implementing `Component` manually, you must now provide a type
for `Mutability`. The type `Mutable` provides equivalent behaviour to
earlier versions of `Component`:
```rust
impl Component for Foo {
type Mutability = Mutable;
// ...
}
```
- When working with generic components, you may need to specify that
your generic parameter implements `Component<Mutability = Mutable>`
rather than `Component` if you require mutable access to said component.
- The entity entry API has had to have some changes made to minimise
friction when working with immutable components. Methods which
previously returned a `Mut<T>` will now typically return an
`OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get
the `Mut<T>` item again.
## Draft Release Notes
Components can now be made immutable while stored within the ECS.
Components are the fundamental unit of data within an ECS, and Bevy
provides a number of ways to work with them that align with Rust's rules
around ownership and borrowing. One part of this is hooks, which allow
for defining custom behavior at key points in a component's lifecycle,
such as addition and removal. However, there is currently no way to
respond to _mutation_ of a component using hooks. The reasons for this
are quite technical, but to summarize, their addition poses a
significant challenge to Bevy's core promises around performance.
Without mutation hooks, it's relatively trivial to modify a component in
such a way that breaks invariants it intends to uphold. For example, you
can use `core::mem::swap` to swap the components of two entities,
bypassing the insertion and removal hooks.
This means the only way to react to this modification is via change
detection in a system, which then begs the question of what happens
_between_ that alteration and the next run of that system?
Alternatively, you could make your component private to prevent
mutation, but now you need to provide commands and a custom `QueryData`
implementation to allow users to interact with your component at all.
Immutable components solve this problem by preventing the creation of an
exclusive reference to the component entirely. Without an exclusive
reference, the only way to modify an immutable component is via removal
or replacement, which is fully captured by component hooks. To make a
component immutable, simply add `#[component(immutable)]`:
```rust
#[derive(Component)]
#[component(immutable)]
struct Foo {
// ...
}
```
When implementing `Component` manually, there is an associated type
`Mutability` which controls this behavior:
```rust
impl Component for Foo {
type Mutability = Mutable;
// ...
}
```
Note that this means when working with generic components, you may need
to specify that a component is mutable to gain access to certain
methods:
```rust
// Before
fn bar<C: Component>() {
// ...
}
// After
fn bar<C: Component<Mutability = Mutable>>() {
// ...
}
```
With this new tool, creating index components, or caching data on an
entity should be more user friendly, allowing libraries to provide APIs
relying on components and hooks to uphold their invariants.
## Notes
- ~~I've done my best to implement this feature, but I'm not happy with
how reflection has turned out. If any reflection SMEs know a way to
improve this situation I'd greatly appreciate it.~~ There is an
outstanding issue around the fallibility of mutable methods on
`ReflectComponent`, but the DX is largely unchanged from `main` now.
- I've attempted to prevent all safe mutable access to a component that
does not implement `Component<Mutability = Mutable>`, but there may
still be some methods I have missed. Please indicate so and I will
address them, as they are bugs.
- Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever
you do with unsafe is between you and your compiler.
- I am marking this PR as ready, but I suspect it will undergo fairly
major revisions based on SME feedback.
- I've marked this PR as _Uncontroversial_ based on the feature, not the
implementation.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Co-authored-by: Nuutti Kotivuori <naked@iki.fi>
# Objective
The `UiBoxShadowSamples` resource should be renamed to
`BoxShadowSamples` so it matches the `BoxShadow` component.
## Migration Guide
`UiBoxShadowSamples` has been renamed to `BoxShadowSamples`
# Objective
Add support for multiple box shadows on a single `Node`.
## Solution
* Rename `BoxShadow` to `ShadowStyle` and remove its `Component` derive.
* Create a new `BoxShadow` component that newtypes a `Vec<ShadowStyle>`.
* Add a `new` constructor method to `BoxShadow` for single shadows.
* Change `extract_shadows` to iterate through a list of shadows per
node.
Render order is determined implicitly from the order of the shadows
stored in the `BoxShadow` component, back-to-front.
Might be more efficient to use a `SmallVec<[ShadowStyle; 1]>` for the
list of shadows but not sure if the extra friction is worth it.
## Testing
Added a node with four differently coloured shadows to the `box_shadow`
example.
---
## Showcase
```
cargo run --example box_shadow
```
<img width="460" alt="four-shadow"
src="https://github.com/user-attachments/assets/2f728c47-33b4-42e1-96ba-28a774b94b24">
## Migration Guide
Bevy UI now supports multiple shadows per node. A new struct
`ShadowStyle` is used to set the style for each shadow. And the
`BoxShadow` component is changed to a tuple struct wrapping a vector
containing a list of `ShadowStyle`s. To spawn a node with a single
shadow you can use the `new` constructor function:
```rust
commands.spawn((
Node::default(),
BoxShadow::new(
Color::BLACK.with_alpha(0.8),
Val::Percent(offset.x),
Val::Percent(offset.y),
Val::Percent(spread),
Val::Px(blur),
)
));
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
In c5742ff43e ZIndex::Local() and
ZIndex::Global() were replaced with ZIndex() and GlobalZIndex().
A comment was likely forgotten.
## Solution
- Remove the deprecated "::Local" in the comment.
# Objective
We switch back and forwards between logical and physical coordinates all
over the place. Systems have to query for cameras and the UiScale when
they shouldn't need to. It's confusing and fragile and new scale factor
bugs get found constantly.
## Solution
* Use physical coordinates whereever possible in `bevy_ui`.
* Store physical coords in `ComputedNode` and tear out all the unneeded
scale factor calculations and queries.
* Add an `inverse_scale_factor` field to `ComputedNode` and set nodes
changed when their scale factor changes.
## Migration Guide
`ComputedNode`'s fields and methods now use physical coordinates.
`ComputedNode` has a new field `inverse_scale_factor`. Multiplying the
physical coordinates by the `inverse_scale_factor` will give the logical
values.
---------
Co-authored-by: atlv <email@atlasdostal.com>
# Objective
Fixes#15940
## Solution
Remove the `pub use` and fix the compile errors.
Make `bevy_image` available as `bevy::image`.
## Testing
Feature Frenzy would be good here! Maybe I'll learn how to use it if I
have some time this weekend, or maybe a reviewer can use it.
## Migration Guide
Use `bevy_image` instead of `bevy_render::texture` items.
---------
Co-authored-by: chompaa <antony.m.3012@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
- Fixes#16292
## Solution
- Renames the `ColorText` marker to `AnimatedText`, which is more
distinct from the `TextColor` Bevy component.
- Changes the comment language from `A unit struct` to `Marker struct`
for better consistency with other Bevy docs.
## Testing
- Locally, example still runs just fine
# Objective
- Fixes#16235
## Solution
- Both Bevy and AccessKit export a `Node` struct, to reduce confusion
Bevy will no longer re-export `AccessKit` from `bevy_a11y`
## Testing
- Tested locally
## Migration Guide
```diff
# main.rs
-- use bevy_a11y::{
-- accesskit::{Node, Rect, Role},
-- AccessibilityNode,
-- };
++ use bevy_a11y::AccessibilityNode;
++ use accesskit::{Node, Rect, Role};
# Cargo.toml
++ accesskit = "0.17"
```
- Users will need to add `accesskit = "0.17"` to the dependencies
section of their `Cargo.toml` file and update their `accesskit` use
statements to come directly from the external crate instead of
`bevy_a11y`.
- Make sure to keep the versions of `accesskit` aligned with the
versions Bevy uses.
# Objective
- Bumps accesskit and accesskit_winit dependencies
## Solution
- Fixes several breaking API changes introduced in accesskit 0.23.
## Testing
- Tested with the ui example and seems to work comparably
# Objective
Expand the `ui` example to show off more of the features and make it
more useful for debugging.
# Solution
Added some extra elements to the `ui` example demonstrating outlines,
border-radius, rotation, image sizing and image flipping.
## Showcase
<img width="961" alt="uiexample"
src="https://github.com/user-attachments/assets/fb0cfb57-9102-4c6c-bc8e-03d3fa6e0bf6">
# Objective
1. UI texture slicing chops and scales an image to fit the size of a
node and isn't meant to place any constraints on the size of the node
itself, but because the required components changes required `ImageSize`
and `ContentSize` for nodes with `UiImage`, texture sliced nodes are
laid out using an `ImageMeasure`.
2. In 0.14 users could spawn a `(UiImage, NodeBundle)` which would
display an image stretched to fill the UI node's bounds ignoring the
image's instrinsic size. Now that `UiImage` requires `ContentSize`,
there's no option to display an image without its size placing
constrains on the UI layout (unless you force the `Node` to a fixed
size, but that's not a solution).
3. It's desirable that the `Sprite` and `UiImage` share similar APIs.
Fixes#16109
## Solution
* Remove the `Component` impl from `ImageScaleMode`.
* Add a `Stretch` variant to `ImageScaleMode`.
* Add a field `scale_mode: ImageScaleMode` to `Sprite`.
* Add a field `mode: UiImageMode` to `UiImage`.
* Add an enum `UiImageMode` similar to `ImageScaleMode` but with
additional UI specific variants.
* Remove the queries for `ImageScaleMode` from Sprite and UI extraction,
and refer to the new fields instead.
* Change `ui_layout_system` to update measure funcs on any change to
`ContentSize`s to enable manual clearing without removing the component.
* Don't add a measure unless `UiImageMode::Auto` is set in
`update_image_content_size_system`. Mutably deref the `Mut<ContentSize>`
if the `UiImage` is changed to force removal of any existing measure
func.
## Testing
Remove all the constraints from the ui_texture_slice example:
```rust
//! This example illustrates how to create buttons with their textures sliced
//! and kept in proportion instead of being stretched by the button dimensions
use bevy::{
color::palettes::css::{GOLD, ORANGE},
prelude::*,
winit::WinitSettings,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
// Only run the app when there is user input. This will significantly reduce CPU/GPU use.
.insert_resource(WinitSettings::desktop_app())
.add_systems(Startup, setup)
.add_systems(Update, button_system)
.run();
}
fn button_system(
mut interaction_query: Query<
(&Interaction, &Children, &mut UiImage),
(Changed<Interaction>, With<Button>),
>,
mut text_query: Query<&mut Text>,
) {
for (interaction, children, mut image) in &mut interaction_query {
let mut text = text_query.get_mut(children[0]).unwrap();
match *interaction {
Interaction::Pressed => {
**text = "Press".to_string();
image.color = GOLD.into();
}
Interaction::Hovered => {
**text = "Hover".to_string();
image.color = ORANGE.into();
}
Interaction::None => {
**text = "Button".to_string();
image.color = Color::WHITE;
}
}
}
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let image = asset_server.load("textures/fantasy_ui_borders/panel-border-010.png");
let slicer = TextureSlicer {
border: BorderRect::square(22.0),
center_scale_mode: SliceScaleMode::Stretch,
sides_scale_mode: SliceScaleMode::Stretch,
max_corner_scale: 1.0,
};
// ui camera
commands.spawn(Camera2d);
commands
.spawn(Node {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
})
.with_children(|parent| {
for [w, h] in [[150.0, 150.0], [300.0, 150.0], [150.0, 300.0]] {
parent
.spawn((
Button,
Node {
// width: Val::Px(w),
// height: Val::Px(h),
// horizontally center child text
justify_content: JustifyContent::Center,
// vertically center child text
align_items: AlignItems::Center,
margin: UiRect::all(Val::Px(20.0)),
..default()
},
UiImage::new(image.clone()),
ImageScaleMode::Sliced(slicer.clone()),
))
.with_children(|parent| {
// parent.spawn((
// Text::new("Button"),
// TextFont {
// font: asset_server.load("fonts/FiraSans-Bold.ttf"),
// font_size: 33.0,
// ..default()
// },
// TextColor(Color::srgb(0.9, 0.9, 0.9)),
// ));
});
}
});
}
```
This should result in a blank window, since without any constraints the
texture slice image nodes should be zero-sized. But in main the image
nodes are given the size of the underlying unsliced source image
`textures/fantasy_ui_borders/panel-border-010.png`:
<img width="321" alt="slicing"
src="https://github.com/user-attachments/assets/cbd74c9c-14cd-4b4d-93c6-7c0152bb05ee">
For this PR need to change the lines:
```
UiImage::new(image.clone()),
ImageScaleMode::Sliced(slicer.clone()),
```
to
```
UiImage::new(image.clone()).with_mode(UiImageMode::Sliced(slicer.clone()),
```
and then nothing should be rendered, as desired.
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
1. Nodes with `Display::None` set are removed from the layout and have
no position or size. Outlines should not be drawn for a node with
`Display::None` set.
2. The outline and border colors are checked for transparency together.
If only one of the two is transparent, both will get queued.
3. The `node.is_empty()` check is insufficient to check if a border is
present since a non-zero sized node can have a zero width border.
## Solution
1. Add a check to `extract_uinode_borders` and ignore the node if
`Display::None` is set.
2. Filter the border and outline optional components by
`is_fully_transparent`.
3. Check if all the border widths are zero instead.
## Testing
I added dark cyan outlines around the left and right sections in the
`display_and_visibility` example. If you run the example and set the
outermost node to `Display::None` on the right, then you'll see the that
the outline on the left disappears.
# Objective
Bevy seems to want to standardize on "American English" spellings. Not
sure if this is laid out anywhere in writing, but see also #15947.
While perusing the docs for `typos`, I noticed that it has a `locale`
config option and tried it out.
## Solution
Switch to `en-us` locale in the `typos` config and run `typos -w`
## Migration Guide
The following methods or fields have been renamed from `*dependants*` to
`*dependents*`.
- `ProcessorAssetInfo::dependants`
- `ProcessorAssetInfos::add_dependant`
- `ProcessorAssetInfos::non_existent_dependants`
- `AssetInfo::dependants_waiting_on_load`
- `AssetInfo::dependants_waiting_on_recursive_dep_load`
- `AssetInfos::loader_dependants`
- `AssetInfos::remove_dependants_and_labels`
# Objective
Continue improving the user experience of our UI Node API in the
direction specified by [Bevy's Next Generation Scene / UI
System](https://github.com/bevyengine/bevy/discussions/14437)
## Solution
As specified in the document above, merge `Style` fields into `Node`,
and move "computed Node fields" into `ComputedNode` (I chose this name
over something like `ComputedNodeLayout` because it currently contains
more than just layout info. If we want to break this up / rename these
concepts, lets do that in a separate PR). `Style` has been removed.
This accomplishes a number of goals:
## Ergonomics wins
Specifying both `Node` and `Style` is now no longer required for
non-default styles
Before:
```rust
commands.spawn((
Node::default(),
Style {
width: Val::Px(100.),
..default()
},
));
```
After:
```rust
commands.spawn(Node {
width: Val::Px(100.),
..default()
});
```
## Conceptual clarity
`Style` was never a comprehensive "style sheet". It only defined "core"
style properties that all `Nodes` shared. Any "styled property" that
couldn't fit that mold had to be in a separate component. A "real" style
system would style properties _across_ components (`Node`, `Button`,
etc). We have plans to build a true style system (see the doc linked
above).
By moving the `Style` fields to `Node`, we fully embrace `Node` as the
driving concept and remove the "style system" confusion.
## Next Steps
* Consider identifying and splitting out "style properties that aren't
core to Node". This should not happen for Bevy 0.15.
---
## Migration Guide
Move any fields set on `Style` into `Node` and replace all `Style`
component usage with `Node`.
Before:
```rust
commands.spawn((
Node::default(),
Style {
width: Val::Px(100.),
..default()
},
));
```
After:
```rust
commands.spawn(Node {
width: Val::Px(100.),
..default()
});
```
For any usage of the "computed node properties" that used to live on
`Node`, use `ComputedNode` instead:
Before:
```rust
fn system(nodes: Query<&Node>) {
for node in &nodes {
let computed_size = node.size();
}
}
```
After:
```rust
fn system(computed_nodes: Query<&ComputedNode>) {
for computed_node in &computed_nodes {
let computed_size = computed_node.size();
}
}
```
# Objective
As discussed in #15341, ghost nodes are a contentious and experimental
feature. In the interest of enabling ecosystem experimentation, we've
decided to keep them in Bevy 0.15.
That said, we don't use them internally, and don't expect third-party
crates to support them. If the experimentation returns a negative result
(they aren't very useful, an alternative design is preferred etc) they
will be removed.
We should clearly communicate this status to users, and make sure that
users don't use ghost nodes in their projects without a very clear
understanding of what they're getting themselves into.
## Solution
To make life easy for users (and Bevy), `GhostNode` and all associated
helpers remain public and are always available.
However, actually constructing these requires enabling a feature flag
that's clearly marked as experimental. To do so, I've added a
meaningless private field.
When the feature flag is enabled, our constructs (`new` and `default`)
can be used. I've added a `new` constructor, which should be preferred
over `Default::default` as that can be readily deprecated, allowing us
to prompt users to swap over to the much nicer `GhostNode` syntax once
this is a unit struct again.
Full credit: this was mostly @cart's design: I'm just implementing it!
## Testing
I've run the ghost_nodes example and it fails to compile without the
feature flag. With the feature flag, it works fine :)
---------
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
Fixes#15834
## Migration Guide
The APIs of `Time`, `Timer` and `Stopwatch` have been cleaned up for
consistency with each other and the standard library's `Duration` type.
The following methods have been renamed:
- `Stowatch::paused` -> `Stopwatch::is_paused`
- `Time::elapsed_seconds` -> `Time::elasped_secs` (including `_f64` and
`_wrapped` variants)
# Objective
Limited implementation of the CSS property `overflow-clip-margin`
https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-margin
Allows you to control the visible area for clipped content when using
overfllow-clip, -hidden, or -scroll and expand it with a margin.
Based on #15442Fixes#15468
## Solution
Adds a new field to Style: `overflow_clip_margin: OverflowClipMargin`.
The field is ignored unless overflow-clip, -hidden or -scroll is set on
at least one axis.
`OverflowClipMargin` has these associated constructor functions:
```
pub const fn content_box() -> Self;
pub const fn padding_box() -> Self;
pub const fn border_box() -> Self;
```
You can also use the method `with_margin` to increases the size of the
visible area:
```
commands
.spawn(NodeBundle {
style: Style {
width: Val::Px(100.),
height: Val::Px(100.),
padding: UiRect::all(Val::Px(20.)),
border: UiRect::all(Val::Px(5.)),
overflow: Overflow::clip(),
overflow_clip_margin: OverflowClipMargin::border_box().with_margin(25.),
..Default::default()
},
border_color: Color::BLACK.into(),
background_color: GRAY.into(),
..Default::default()
})
```
`with_margin` expects a length in logical pixels, negative values are
clamped to zero.
## Notes
* To keep this PR as simple as possible I omitted responsive margin
values support. This could be added in a follow up if we want it.
* CSS also supports a `margin-box` option but we don't have access to
the margin values in `Node` so it's probably not feasible to implement
atm.
## Testing
```cargo run --example overflow_clip_margin```
<img width="396" alt="overflow-clip-margin" src="https://github.com/user-attachments/assets/07b51cd6-a565-4451-87a0-fa079429b04b">
## Migration Guide
Style has a new field `OverflowClipMargin`. It allows users to set the visible area for clipped content when using overflow-clip, -hidden, or -scroll and expand it with a margin.
There are three associated constructor functions `content_box`, `padding_box` and `border_box`:
* `content_box`: elements painted outside of the content box area (the innermost part of the node excluding the padding and border) of the node are clipped. This is the new default behaviour.
* `padding_box`: elements painted outside outside of the padding area of the node are clipped.
* `border_box`: elements painted outside of the bounds of the node are clipped. This matches the behaviour from Bevy 0.14.
There is also a `with_margin` method that increases the size of the visible area by the given number in logical pixels, negative margin values are clamped to zero.
`OverflowClipMargin` is ignored unless overflow-clip, -hidden or -scroll is also set on at least one axis of the UI node.
---------
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
# Objective
Cleanup naming and docs, add missing migration guide after #15591
All text root nodes now use `Text` (UI) / `Text2d`.
All text readers/writers use `Text<Type>Reader`/`Text<Type>Writer`
convention.
---
## Migration Guide
Doubles as #15591 migration guide.
Text bundles (`TextBundle` and `Text2dBundle`) were removed in favor of
`Text` and `Text2d`.
Shared configuration fields were replaced with `TextLayout`, `TextFont`
and `TextColor` components.
Just `TextBundle`'s additional field turned into `TextNodeFlags`
component,
while `Text2dBundle`'s additional fields turned into `TextBounds` and
`Anchor` components.
Text sections were removed in favor of hierarchy-based approach.
For root text entities with `Text` or `Text2d` components, child
entities with `TextSpan` will act as additional text sections.
To still access text spans by index, use the new `TextUiReader`,
`Text2dReader` and `TextUiWriter`, `Text2dWriter` system parameters.
# Objective
Change UI clipping to respect borders and padding.
Fixes#15335
## Solution
Based on #15163
1. Add a `padding` field to `Node`.
2. In `ui_layout_size` copy the padding values from taffy to
`Node::padding`.
4. Determine the node's content box (The innermost part of the node
excluding the padding and border).
5. In `update_clipping` perform the clipping intersection with the
node's content box.
## Notes
* `Rect` probably needs some helper methods for working with insets but
because `Rect` and `BorderRect` are in different crates it's awkward to
add them. Left for a follow up.
* We could have another `Overflow` variant (probably called
`Overflow::Hidden`) to that clips inside of the border box instead of
the content box. Left it out here as I'm not certain about the naming or
behaviour though. If this PR is adopted, it would be trivial to add a
`Hidden` variant in a follow up.
* Depending on UI scaling there are sometimes gaps in the layout:
<img width="532" alt="rounding-bug"
src="https://github.com/user-attachments/assets/cc29aa0d-44fe-403f-8f0e-cd28a8b1d1b3">
This is caused by existing bugs in `ui_layout_system`'s coordinates
rounding and not anything to do with the changes in this PR.
## Testing
This PR also changes the `overflow` example to display borders on the
overflow nodes so you can see how this works:
#### main (The image is clipped at the edges of the node, overwriting
the border).
<img width="722" alt="main_overflow"
src="https://github.com/user-attachments/assets/eb316cd0-fff8-46ee-b481-e0cd6bab3f5c">
#### this PR (The image is clipped at the edges of the node's border).
<img width="711" alt="content-box-clip"
src="https://github.com/user-attachments/assets/fb302e56-9302-47b9-9a29-ec3e15fe9a9f">
## Migration Guide
Migration guide is on #15561
---------
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
# Objective
- closes#15866
## Solution
- Simply migrate where possible.
## Testing
- Expect that CI will do most of the work. Examples is another way of
testing this, as most of the work is in that area.
---
## Notes
For now, this PR doesn't migrate `QueryState::single` and friends as for
now, this look like another issue. So for example, QueryBuilders that
used single or `World::query` that used single wasn't migrated. If there
is a easy way to migrate those, please let me know.
Most of the uses of `Query::single` were removed, the only other uses
that I found was related to tests of said methods, so will probably be
removed when we remove `Query::single`.
# Objective
Currently text is recomputed unnecessarily on any changes to its color,
which is extremely expensive.
## Solution
Split up `TextStyle` into two separate components `TextFont` and
`TextColor`.
## Testing
I added this system to `many_buttons`:
```rust
fn set_text_colors_changed(mut colors: Query<&mut TextColor>) {
for mut text_color in colors.iter_mut() {
text_color.set_changed();
}
}
```
reports ~4fps on main, ~50fps with this PR.
## Migration Guide
`TextStyle` has been renamed to `TextFont` and its `color` field has
been moved to a separate component named `TextColor` which newtypes
`Color`.
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
- Closes#15720
## Solution
Wrap the handle in a new wrapper component: `UiMaterialHandle`
It's not possible to match the naming convention of `MeshMaterial3d/2d`
here with the trait already being called `UiMaterial`
Should we consider renaming to `Material3d/2dHandle` and `Mesh3d/2d` to
`Mesh3d/2dHandle`?
- This shouldn't have any merge conflicts with #15591
## Testing
Tested the `ui_material` example
## Migration Guide
Let's defer the migration guide to the required component port. I just
want to yeet the `Component` impl on `Handle` in the meantime :)
# Objective
UI box shadow support
Adds a new component `BoxShadow`:
```rust
pub struct BoxShadow {
/// The shadow's color
pub color: Color,
/// Horizontal offset
pub x_offset: Val,
/// Vertical offset
pub y_offset: Val,
/// Horizontal difference in size from the occluding uninode
pub spread_radius: Val,
/// Blurriness of the shadow
pub blur_radius: Val,
}
```
To use `BoxShadow`, add the component to any Bevy UI node and a shadow
will be drawn beneath that node.
Also adds a resource `BoxShadowSamples` that can be used to adjust the
shadow quality.
#### Notes
* I'm not super happy with the field names. Maybe we need a `struct Size
{ width: Val, height: Val }` type or something.
* The shader isn't very optimised but I don't see that it's too
important for now as the number of shadows being rendered is not going
to be massive most of the time. I think it's more important to get the
API and geometry correct with this PR.
* I didn't implement an inset property, it's not essential and can
easily be added in a follow up.
* Shadows are only rendered for uinodes, not for images or text.
* Batching isn't supported, it would need out-of-the-scope-of-this-pr
changes to the way the UI handles z-ordering for it to be effective.
# Showcase
```cargo run --example box_shadow -- --samples 4```
<img width="391" alt="br" src="https://github.com/user-attachments/assets/4e8add96-dc93-46e0-9e35-d995eb0943ad">
```cargo run --example box_shadow -- --samples 10```
<img width="391" alt="s10"
src="https://github.com/user-attachments/assets/ecb384c9-4012-4cd6-9dea-5180904bf28e">
# Objective
- Rename `Pickable` to `PickingBehavior` to counter the easily-made
assumption that the component is required. It is optional
- Fix and clarify documentation
- The docs in `crates/bevy_ui/src/picking_backend.rs` were incorrect
about the necessity of `Pickable`
- Plus two minor code quality changes in this commit
(7c2e75f48d)
Closes#15632
# Objective
Add a background colour to each text node in the `text_debug` example to
visualize their bounds.
## Showcase
<img width="961" alt="deb"
src="https://github.com/user-attachments/assets/deec3e15-b0f0-411f-9af1-597587ac2a83">
In the bottom right you can see the empty space at the bottom of the
text node, making it much more obvious that there is a bug causing the
size of the bounds to be calculated incorrectly.
# Objective
Yet another PR for migrating stuff to required components. This time,
cameras!
## Solution
As per the [selected
proposal](https://hackmd.io/tsYID4CGRiWxzsgawzxG_g#Combined-Proposal-1-Selected),
deprecate `Camera2dBundle` and `Camera3dBundle` in favor of `Camera2d`
and `Camera3d`.
Adding a `Camera` without `Camera2d` or `Camera3d` now logs a warning,
as suggested by Cart [on
Discord](https://discord.com/channels/691052431525675048/1264881140007702558/1291506402832945273).
I would personally like cameras to work a bit differently and be split
into a few more components, to avoid some footguns and confusing
semantics, but that is more controversial, and shouldn't block this core
migration.
## Testing
I ran a few 2D and 3D examples, and tried cameras with and without
render graphs.
---
## Migration Guide
`Camera2dBundle` and `Camera3dBundle` have been deprecated in favor of
`Camera2d` and `Camera3d`. Inserting them will now also insert the other
components required by them automatically.
# Objective
- Fixes#14826
- For context, see #15238
## Solution
Add a `GhostNode` component to `bevy_ui` and update all the relevant
systems to use it to traverse for UI children.
- [x] `ghost_hierarchy` module
- [x] Add `GhostNode`
- [x] Add `UiRootNodes` system param for iterating (ghost-aware) UI root
nodes
- [x] Add `UiChildren` system param for iterating (ghost-aware) UI
children
- [x] Update `layout::ui_layout_system`
- [x] Use ghost-aware root nodes for camera updates
- [x] Update and remove children in taffy
- [x] Initial spawn
- [x] Detect changes on nested UI children
- [x] Use ghost-aware children traversal in
`update_uinode_geometry_recursive`
- [x] Update the rest of the UI systems to use the ghost hierarchy
- [x] `stack::ui_stack_system`
- [x] `update::`
- [x] `update_clipping_system`
- [x] `update_target_camera_system`
- [x] `accessibility::calc_name`
## Testing
- [x] Added a new example `ghost_nodes` that can be used as a testbed.
- [x] Added unit tests for _some_ of the traversal utilities in
`ghost_hierarchy`
- [x] Ensure this fulfills the needs for currently known use cases
- [x] Reactivity libraries (test with `bevy_reactor`)
- [ ] Text spans (mentioned by koe [on
discord](https://discord.com/channels/691052431525675048/1285371432460881991/1285377442998915246))
---
## Performance
[See comment
below](https://github.com/bevyengine/bevy/pull/15341#issuecomment-2385456820)
## Migration guide
Any code that previously relied on `Parent`/`Children` to iterate UI
children may now want to use `bevy_ui::UiChildren` to ensure ghost nodes
are skipped, and their first descendant Nodes included.
UI root nodes may now be children of ghost nodes, which means
`Without<Parent>` might not query all root nodes. Use
`bevy_ui::UiRootNodes` where needed to iterate root nodes instead.
## Potential future work
- Benchmarking/optimizations of hierarchies containing lots of ghost
nodes
- Further exploration of UI hierarchies and markers for root nodes/leaf
nodes to create better ergonomics for things like `UiLayer` (world-space
ui)
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
# Objective
- Improve code quality in preparation for
https://github.com/bevyengine/bevy/discussions/15014
## Solution
- Rename BreakLineOn to LineBreak.
## Migration Guide
`BreakLineOn` was renamed to `LineBreak`, and paramters named
`linebreak_behavior` were renamed to `linebreak`.
# Objective
A big step in the migration to required components: meshes and
materials!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ):
- Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle`.
- Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`.
- Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`,
which wrap a `Handle<M>`.
- Meshes *without* a mesh material should be rendered with a default
material. The existence of a material is determined by
`HasMaterial2d`/`HasMaterial3d`, which is required by
`MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the
generics.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, previously nothing was rendered. Now,
it renders a white default `ColorMaterial` in 2D and a
`StandardMaterial` in 3D (this can be overridden). Below, only every
other entity has a material:
![Näyttökuva 2024-09-29
181746](https://github.com/user-attachments/assets/5c8be029-d2fe-4b8c-ae89-17a72ff82c9a)
![Näyttökuva 2024-09-29
181918](https://github.com/user-attachments/assets/58adbc55-5a1e-4c7d-a2c7-ed456227b909)
Why white? This is still open for discussion, but I think white makes
sense for a *default* material, while *invalid* asset handles pointing
to nothing should have something like a pink material to indicate that
something is broken (I don't handle that in this PR yet). This is kind
of a mix of Godot and Unity: Godot just renders a white material for
non-existent materials, while Unity renders nothing when no materials
exist, but renders pink for invalid materials. I can also change the
default material to pink if that is preferable though.
## Testing
I ran some 2D and 3D examples to test if anything changed visually. I
have not tested all examples or features yet however. If anyone wants to
test more extensively, it would be appreciated!
## Implementation Notes
- The relationship between `bevy_render` and `bevy_pbr` is weird here.
`bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all
of the material logic, and `bevy_render` doesn't depend on it. I feel
like the two crates should be refactored in some way, but I think that's
out of scope for this PR.
- I didn't migrate meshlets to required components yet. That can
probably be done in a follow-up, as this is already a huge PR.
- It is becoming increasingly clear to me that we really, *really* want
to disallow raw asset handles as components. They caused me a *ton* of
headache here already, and it took me a long time to find every place
that queried for them or inserted them directly on entities, since there
were no compiler errors for it. If we don't remove the `Component`
derive, I expect raw asset handles to be a *huge* footgun for users as
we transition to wrapper components, especially as handles as components
have been the norm so far. I personally consider this to be a blocker
for 0.15: we need to migrate to wrapper components for asset handles
everywhere, and remove the `Component` derive. Also see
https://github.com/bevyengine/bevy/issues/14124.
---
## Migration Guide
Asset handles for meshes and mesh materials must now be wrapped in the
`Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d`
components for 2D and 3D respectively. Raw handles as components no
longer render meshes.
Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle` have been deprecated. Instead, use the mesh and material
components directly.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, a white default material is now used.
Previously, nothing was rendered if the material was missing.
The `WithMesh2d` and `WithMesh3d` query filter type aliases have also
been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
Another step in the migration to required components: lights!
Note that this does not include `EnvironmentMapLight` or reflection
probes yet, because their API hasn't been fully chosen yet.
## Solution
As per the [selected
proposals](https://hackmd.io/@bevy/required_components/%2FLLnzwz9XTxiD7i2jiUXkJg):
- Deprecate `PointLightBundle` in favor of the `PointLight` component
- Deprecate `SpotLightBundle` in favor of the `PointLight` component
- Deprecate `DirectionalLightBundle` in favor of the `DirectionalLight`
component
## Testing
I ran some examples with lights.
---
## Migration Guide
`PointLightBundle`, `SpotLightBundle`, and `DirectionalLightBundle` have
been deprecated. Use the `PointLight`, `SpotLight`, and
`DirectionalLight` components instead. Adding them will now insert the
other components required by them automatically.
# Objective
`ui_stack_system` generates a tree of `StackingContexts` which it then
flattens to get the `UiStack`.
But there's no need to construct a new tree. We can query for nodes with
a global `ZIndex`, add those nodes to the root nodes list and then build
the `UiStack` from a walk of the existing layout tree, ignoring any
branches that have a global `Zindex`.
Fixes#9877
## Solution
Split the `ZIndex` enum into two separate components, `ZIndex` and
`GlobalZIndex`
Query for nodes with a `GlobalZIndex`, add those nodes to the root nodes
list and then build the `UiStack` from a walk of the existing layout
tree, filtering branches by `Without<GlobalZIndex>` so we don't revisit
nodes.
```
cargo run --profile stress-test --features trace_tracy --example many_buttons
```
<img width="672" alt="ui-stack-system-walk-split-enum"
src="https://github.com/bevyengine/bevy/assets/27962798/11e357a5-477f-4804-8ada-c4527c009421">
(Yellow is this PR, red is main)
---
## Changelog
`Zindex`
* The `ZIndex` enum has been split into two separate components `ZIndex`
(which replaces `ZIndex::Local`) and `GlobalZIndex` (which replaces
`ZIndex::Global`). An entity can have both a `ZIndex` and
`GlobalZIndex`, in comparisons `ZIndex` breaks ties if two
`GlobalZIndex` values are equal.
`ui_stack_system`
* Instead of generating a tree of `StackingContexts`, query for nodes
with a `GlobalZIndex`, add those nodes to the root nodes list and then
build the `UiStack` from a walk of the existing layout tree, filtering
branches by `Without<GlobalZIndex` so we don't revisit nodes.
## Migration Guide
The `ZIndex` enum has been split into two separate components `ZIndex`
(which replaces `ZIndex::Local`) and `GlobalZIndex` (which replaces
`ZIndex::Global`). An entity can have both a `ZIndex` and
`GlobalZIndex`, in comparisons `ZIndex` breaks ties if two
`GlobalZindex` values are equal.
---------
Co-authored-by: Gabriel Bourgeois <gabriel.bourgeoisv4si@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
# Objective
- Fixes#6370
- Closes#6581
## Solution
- Added the following lints to the workspace:
- `std_instead_of_core`
- `std_instead_of_alloc`
- `alloc_instead_of_core`
- Used `cargo +nightly fmt` with [item level use
formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A)
to split all `use` statements into single items.
- Used `cargo clippy --workspace --all-targets --all-features --fix
--allow-dirty` to _attempt_ to resolve the new linting issues, and
intervened where the lint was unable to resolve the issue automatically
(usually due to needing an `extern crate alloc;` statement in a crate
root).
- Manually removed certain uses of `std` where negative feature gating
prevented `--all-features` from finding the offending uses.
- Used `cargo +nightly fmt` with [crate level use
formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A)
to re-merge all `use` statements matching Bevy's previous styling.
- Manually fixed cases where the `fmt` tool could not re-merge `use`
statements due to conditional compilation attributes.
## Testing
- Ran CI locally
## Migration Guide
The MSRV is now 1.81. Please update to this version or higher.
## Notes
- This is a _massive_ change to try and push through, which is why I've
outlined the semi-automatic steps I used to create this PR, in case this
fails and someone else tries again in the future.
- Making this change has no impact on user code, but does mean Bevy
contributors will be warned to use `core` and `alloc` instead of `std`
where possible.
- This lint is a critical first step towards investigating `no_std`
options for Bevy.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes#15142
## Solution
* Moved all the UI border geometry calculations that were scattered
through the UI extraction functions into `ui_layout_system`.
* Added a `border: BorderRect` field to `Node` to store the border size
computed by `ui_layout_system`.
* Use the border values returned from Taffy rather than calculate them
ourselves during extraction.
* Removed the `logical_rect` and `physical_rect` methods from `Node` the
descriptions and namings are deceptive, it's better to create the rects
manually instead.
* Added a method `outline_radius` to `Node` that calculates the border
radius of outlines.
* For border values `ExtractedUiNode` takes `BorderRect` and
`ResolvedBorderRadius` now instead of raw `[f32; 4]` values and converts
them in `prepare_uinodes`.
* Removed some unnecessary scaling and clamping of border values
(#15142).
* Added a `BorderRect::ZERO` constant.
* Added an `outlined_node_size` method to `Node`.
## Testing
Added some non-uniform borders to the border example. Everything seems
to be in order:
<img width="626" alt="nub"
src="https://github.com/user-attachments/assets/258ed8b5-1a9e-4ac5-99c2-6bf25c0ef31c">
## Migration Guide
The `logical_rect` and `physical_rect` methods have been removed from
`Node`. Use `Rect::from_center_size` with the translation and node size
instead.
The types of the fields border and border_radius of `ExtractedUiNode`
have been changed to `BorderRect` and `ResolvedBorderRadius`
respectively.
---------
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
Co-authored-by: akimakinai <105044389+akimakinai@users.noreply.github.com>
# Objective
Fixes#15401
## Solution
Changes the scroll inversion hotkey in the example from Shift to
Control.
Shift is idiomatic for this. Since we cannot use Shift per #15401, I
picked another modifier arbitrarily. A production app would handle this
in a platform specific way until the platform behaviors are unified
upstream, but no point here.
## Testing
I don't have a mac readily available for testing, if someone wouldn't
mind testing. I would also appreciate confirmation that trackpad is
working nicely.
# Objective
- Fixes#10720
- Adds the ability to control font smoothing of rendered text
## Solution
- Introduce the `FontSmoothing` enum, with two possible variants
(`FontSmoothing::None` and `FontSmoothing::AntiAliased`):
- This is based on `-webkit-font-smoothing`, in line with our practice
of adopting CSS-like properties/names for UI;
- I could have gone instead for the [`font-smooth`
property](https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)
that's also supported by browsers, but didn't since it's also
non-standard, has an uglier name, and doesn't allow controlling the type
of antialias applied.
- Having an enum instead of e.g. a boolean, leaves the path open for
adding `FontSmoothing::SubpixelAntiAliased` in the future, without a
breaking change;
- Add all the necessary plumbing to get the `FontSmoothing` information
to where we rasterize the glyphs and store them in the atlas;
- Change the font atlas key to also take into account the smoothing
setting, not only font and font size;
- Since COSMIC Text [doesn't support controlling font
smoothing](https://github.com/pop-os/cosmic-text/issues/279), we roll
out our own threshold-based “implementation”:
- This has the downside of **looking ugly for “regular” vector fonts**
⚠️, since it doesn't properly take the hinting information into account
like a proper implementation on the rasterizer side would.
- However, **for fonts that have been specifically authored to be pixel
fonts, (a common use case in games!) this is not as big of a problem**,
since all lines are vertical/horizontal, and close to the final pixel
boundaries (as long as the font is used at a multiple of the size
originally intended by the author)
- Once COSMIC exposes this functionality, we can switch to using it
directly, and get better results;
- Use a nearest neighbor sampler for atlases with font smoothing
disabled, so that you can scale the text via transform and still get the
pixelated look;
- Add a convenience method to `Text` for setting the font smoothing;
- Add a demonstration of using the `FontSmoothing` property to the
`text2d` example.
## Testing
- Did you test these changes? If so, how?
- Yes. Via the `text2d`example, and also in my game.
- Are there any parts that need more testing?
- I'd like help from someone for testing this on devices/OSs with
fractional scaling (Android/Windows)
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Both via the `text2d` example and also by using it directly on your
projects.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- macOS
---
## Showcase
```rust
commands.spawn(Text2dBundle {
text: Text::from_section("Hello, World!", default())
.with_font_smoothing(FontSmoothing::None),
..default()
});
```
![Screenshot 2024-09-22 at 12 33
39](https://github.com/user-attachments/assets/93e19672-b8c0-4cba-a8a3-4525fe2ae1cb)
<img width="740" alt="image"
src="https://github.com/user-attachments/assets/b881b02c-4e43-410b-902f-6985c25140fc">
## Migration Guide
- `Text` now contains a `font_smoothing: FontSmoothing` property, make
sure to include it or add `..default()` when using the struct directly;
- `FontSizeKey` has been renamed to `FontAtlasKey`, and now also
contains the `FontSmoothing` setting;
- The following methods now take an extra `font_smoothing:
FontSmoothing` argument:
- `FontAtlas::new()`
- `FontAtlasSet::add_glyph_to_atlas()`
- `FontAtlasSet::get_glyph_atlas_info()`
- `FontAtlasSet::get_outlined_glyph_texture()`
# Objective
- Fixes#8074
- Adopts / Supersedes #8104
## Solution
Adapted from #8104 and affords the same benefits.
**Additions**
- [x] Update scrolling on relayout (height of node or contents may have
changed)
- [x] Make ScrollPosition component optional for ui nodes to avoid
checking every node on scroll
- [x] Nested scrollviews
**Omissions**
- Removed input handling for scrolling from `bevy_ui`. Users should
update `ScrollPosition` directly.
### Implementation
Adds a new `ScrollPosition` component. Updating this component on a
`Node` with an overflow axis set to `OverflowAxis::Scroll` will
reposition its children by that amount when calculating node transforms.
As before, no impact on the underlying Taffy layout.
Calculating this correctly is trickier than it was in #8104 due to
`"Update scrolling on relayout"`.
**Background**
When `ScrollPosition` is updated directly by the user, it can be
trivially handled in-engine by adding the parent's scroll position to
the final location of each child node. However, _other layout actions_
may result in a situation where `ScrollPosition` needs to be updated.
Consider a 1000 pixel tall vertically scrolling list of 100 elements,
each 100 pixels tall. Scrolled to the bottom, the
`ScrollPosition.offset_y` is 9000, just enough to display the last
element in the list. When removing an element from that list, the new
desired `ScrollPosition.offset_y` is 8900, but, critically, that is not
known until after the sizes and positions of the children of the
scrollable node are resolved.
All user scrolling code today handles this by delaying the resolution by
one frame. One notable disadvantage of this is the inability to support
`WinitSettings::desktop_app()`, since there would need to be an input
AFTER the layout change that caused the scroll position to update for
the results of the scroll position update to render visually.
I propose the alternative in this PR, which allows for same-frame
resolution of scrolling layout.
**Resolution**
_Edit: Below resolution is outdated, and replaced with the simpler usage
of taffy's `Layout::content_size`._
When recursively iterating the children of a node, each child now
returns a `Vec2` representing the location of their own bottom right
corner. Then, `[[0,0, [x,y]]` represents a bounding box containing the
scrollable area filled by that child. Scrollable parents aggregate those
areas into the bounding box of _all_ children, then consider that result
against `ScrollPosition` to ensure its validity.
In the event that resolution of the layout of the children invalidates
the `ScrollPosition` (e.g. scrolled further than there were children to
scroll to), _all_ children of that node must be recursively
repositioned. The position of each child must change as a result of the
change in scroll position.
Therefore, this implementation takes care to only spend the cost of the
"second layout pass" when a specific node actually had a
`ScrollPosition` forcibly updated by the layout of its children.
## Testing
Examples in `ui/scroll.rs`. There may be more complex node/style
interactions that were unconsidered.
---
## Showcase
![scroll](https://github.com/user-attachments/assets/1331138f-93aa-4a8f-959c-6be18a04ff03)
## Alternatives
- `bevy_ui` doesn't support scrolling.
- `bevy_ui` implements scrolling with a one-frame delay on reactions to
layout changes.
# Objective
- Fixes#15319
- Fixes#15317
## Solution
- Updated CI task to check for _any_ `bevy_*` crates, rather than just
`bevy_internal`
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
- Fixes#15236
## Solution
- Use bevy_math::ops instead of std floating point operations.
## Testing
- Did you test these changes? If so, how?
Unit tests and `cargo run -p ci -- test`
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Execute `cargo run -p ci -- test` on Windows.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Windows
## Migration Guide
- Not a breaking change
- Projects should use bevy math where applicable
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: IQuick 143 <IQuick143cz@gmail.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
- Makes naming between add_child and add_children more consistent
- Fixes#15101
## Solution
renamed push_children to add_children
## Testing
- Did you test these changes? If so, how?
Ran tests + grep search for any instance of `push_child`
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
ran tests on WSL2
---
## Migration Guide
> This section is optional. If there are no breaking changes, you can
delete this section.
- If this PR is a breaking change (relative to the last release of
Bevy), describe how a user might need to migrate their code to support
these changes
rename any use of `push_children()` to the updated `add_children()`