Commit graph

753 commits

Author SHA1 Message Date
charlotte
dd812b3e49
Type safe retained render world (#15756)
# Objective

In the Render World, there are a number of collections that are derived
from Main World entities and are used to drive rendering. The most
notable are:
- `VisibleEntities`, which is generated in the `check_visibility` system
and contains visible entities for a view.
- `ExtractedInstances`, which maps entity ids to asset ids.

In the old model, these collections were trivially kept in sync -- any
extracted phase item could look itself up because the render entity id
was guaranteed to always match the corresponding main world id.

After #15320, this became much more complicated, and was leading to a
number of subtle bugs in the Render World. The main rendering systems,
i.e. `queue_material_meshes` and `queue_material2d_meshes`, follow a
similar pattern:

```rust
for visible_entity in visible_entities.iter::<With<Mesh2d>>() {
    let Some(mesh_instance) = render_mesh_instances.get_mut(visible_entity) else {
        continue;
    };
            
    // Look some more stuff up and specialize the pipeline...
            
    let bin_key = Opaque2dBinKey {
        pipeline: pipeline_id,
        draw_function: draw_opaque_2d,
        asset_id: mesh_instance.mesh_asset_id.into(),
        material_bind_group_id: material_2d.get_bind_group_id().0,
    };
    opaque_phase.add(
        bin_key,
        *visible_entity,
        BinnedRenderPhaseType::mesh(mesh_instance.automatic_batching),
    );
}
```

In this case, `visible_entities` and `render_mesh_instances` are both
collections that are created and keyed by Main World entity ids, and so
this lookup happens to work by coincidence. However, there is a major
unintentional bug here: namely, because `visible_entities` is a
collection of Main World ids, the phase item being queued is created
with a Main World id rather than its correct Render World id.

This happens to not break mesh rendering because the render commands
used for drawing meshes do not access the `ItemQuery` parameter, but
demonstrates the confusion that is now possible: our UI phase items are
correctly being queued with Render World ids while our meshes aren't.

Additionally, this makes it very easy and error prone to use the wrong
entity id to look up things like assets. For example, if instead we
ignored visibility checks and queued our meshes via a query, we'd have
to be extra careful to use `&MainEntity` instead of the natural
`Entity`.

## Solution

Make all collections that are derived from Main World data use
`MainEntity` as their key, to ensure type safety and avoid accidentally
looking up data with the wrong entity id:

```rust
pub type MainEntityHashMap<V> = hashbrown::HashMap<MainEntity, V, EntityHash>;
```

Additionally, we make all `PhaseItem` be able to provide both their Main
and Render World ids, to allow render phase implementors maximum
flexibility as to what id should be used to look up data.

You can think of this like tracking at the type level whether something
in the Render World should use it's "primary key", i.e. entity id, or
needs to use a foreign key, i.e. `MainEntity`.

## Testing

##### TODO:

This will require extensive testing to make sure things didn't break!
Additionally, some extraction logic has become more complicated and
needs to be checked for regressions.

## Migration Guide

With the advent of the retained render world, collections that contain
references to `Entity` that are extracted into the render world have
been changed to contain `MainEntity` in order to prevent errors where a
render world entity id is used to look up an item by accident. Custom
rendering code may need to be changed to query for `&MainEntity` in
order to look up the correct item from such a collection. Additionally,
users who implement their own extraction logic for collections of main
world entity should strongly consider extracting into a different
collection that uses `MainEntity` as a key.

Additionally, render phases now require specifying both the `Entity` and
`MainEntity` for a given `PhaseItem`. Custom render phases should ensure
`MainEntity` is available when queuing a phase item.
2024-10-10 18:47:04 +00:00
ickshonpe
15072d7937
Dark text colors fix (#15794)
# Objective

Fix the text colors.

fixes #15788

## Solution

Add the missing corner flags.
2024-10-09 22:14:36 +00:00
UkoeHB
a6be9b4ccd
Rename TextBlock to TextLayout (#15797)
# Objective

- Improve clarity when spawning a text block. See [this
discussion](https://github.com/bevyengine/bevy/pull/15591/#discussion_r1787083571).

## Solution

- Rename `TextBlock` to `TextLayout`.
2024-10-09 20:58:27 +00:00
UkoeHB
c2c19e5ae4
Text rework (#15591)
**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>
2024-10-09 18:35:36 +00:00
Zachary Harrold
c9e41ef552
Remove thiserror from bevy_ui (#15760)
# Objective

- Contributes to #15460

## Solution

- Removed `thiserror` from `bevy_ui`
2024-10-09 14:27:53 +00:00
ickshonpe
a7ed13ad17
Add register_type for UiAntiAlias (#15783)
# Objective

Add `register_type` for `UiAntiAlias`
2024-10-09 14:04:26 +00:00
Tim
57c297becc
Add register_type for UiMaterialHandle and AnimationGraphHandle (#15784) 2024-10-09 14:04:06 +00:00
ickshonpe
675f8ad403
Improved text batching (#14848)
# Objective

The UI text rendering is really slow because it extracts each glyph as a
separate ui node even though all the glyphs in a text section have the
same texture, color and clipping rects.

## Solution

Store the glyphs in a seperate contiguous array, queue one transparent
ui item per text section which has indices into the glyph array.

## Testing

```cargo run --example many_glyphs --release```

Runs at about 22fps on main and 95fps with this PR on my computer.

I'll do some proper comparisons once I work out why tracy 11 is refusing to run.

---------

Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2024-10-08 22:24:27 +00:00
Kristoffer Søholm
2d1b4939d2
Synchronize removed components with the render world (#15582)
# Objective

Fixes #15560
Fixes (most of) #15570

Currently a lot of examples (and presumably some user code) depend on
toggling certain render features by adding/removing a single component
to an entity, e.g. `SpotLight` to toggle a light. Because of the
retained render world this no longer works: Extract will add any new
components, but when it is removed the entity persists unchanged in the
render world.

## Solution

Add `SyncComponentPlugin<C: Component>` that registers
`SyncToRenderWorld` as a required component for `C`, and adds a
component hook that will clear all components from the render world
entity when `C` is removed. We add this plugin to
`ExtractComponentPlugin` which fixes most instances of the problem. For
custom extraction logic we can manually add `SyncComponentPlugin` for
that component.

We also rename `WorldSyncPlugin` to `SyncWorldPlugin` so we start a
naming convention like all the `Extract` plugins.

In this PR I also fixed a bunch of breakage related to the retained
render world, stemming from old code that assumed that `Entity` would be
the same in both worlds.

I found that using the `RenderEntity` wrapper instead of `Entity` in
data structures when referring to render world entities makes intent
much clearer, so I propose we make this an official pattern.

## Testing

Run examples like

```
cargo run --features pbr_multi_layer_material_textures --example clearcoat
cargo run --example volumetric_fog
```

and see that they work, and that toggles work correctly. But really we
should test every single example, as we might not even have caught all
the breakage yet.

---

## Migration Guide

The retained render world notes should be updated to explain this edge
case and `SyncComponentPlugin`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Trashtalk217 <trashtalk217@gmail.com>
2024-10-08 22:23:17 +00:00
Alice Cecile
2f63ebc9c9
Remove warning for children in UI hierarchies without Style (#15736)
# Objective

As discussed in #15591, this warning prevents us from storing leaf nodes
without a `Style` component. Because text sections (as distinct
entities) should not be laid out using `taffy`, this warning is
incorrect.

Users may also have other uses for doing this, and this should generally
increase flexibility without posing particularly serious correctness
concerns.

## Solution

- removed warning about non-UI children with UI parents
- improved the warning about UI parents with non-UI parents
- this warning should stay, for now, as it results in a genuine failure
to perform `taffy` layout
- that said, we should be clearer about the cause and potentially
harmful results of this!
   
## Testing

I inserted an empty entity into the hierarchy in the `button` example as
a leaf node, and it ran with no warnings.
2024-10-08 19:51:47 +00:00
Tim
9aef71bd9b
Replace Handle<M: UiMaterial> component with UiMaterialHandle wrapper (#15740)
# 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 :)
2024-10-08 19:07:58 +00:00
ickshonpe
99b9a2fcd7
box shadow (#15204)
# 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">
2024-10-08 16:26:17 +00:00
Peter Hayman
1c3dee4a6c
fix: register_type::<ScrollPosition> (#15721)
# Objective

- register types that derive reflect.
2024-10-08 16:07:09 +00:00
Tim
d454db8e58
Rename the Pickable component and fix incorrect documentation (#15707)
# 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
2024-10-07 17:09:57 +00:00
Trashtalk217
d1bd46d45e
Deprecate get_or_spawn (#15652)
# Objective

After merging retained rendering world #15320, we now have a good way of
creating a link between worlds (*HIYAA intensifies*). This means that
`get_or_spawn` is no longer necessary for that function. Entity should
be opaque as the warning above `get_or_spawn` says. This is also part of
#15459.

I'm deprecating `get_or_spawn_batch` in a different PR in order to keep
the PR small in size.

## Solution

Deprecate `get_or_spawn` and replace it with `get_entity` in most
contexts. If it's possible to query `&RenderEntity`, then the entity is
synced and `render_entity.id()` is initialized in the render world.

## Migration Guide

If you are given an `Entity` and you want to do something with it, use
`Commands.entity(...)` or `World.entity(...)`. If instead you want to
spawn something use `Commands.spawn(...)` or `World.spawn(...)`. If you
are not sure if an entity exists, you can always use `get_entity` and
match on the `Option<...>` that is returned.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-10-07 16:08:22 +00:00
Joona Aalto
25bfa80e60
Migrate cameras to required components (#15641)
# 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.
2024-10-05 01:59:52 +00:00
vero
0b9a461d5d
Invert the dependency between bevy_animation and bevy_ui (#15634)
# Objective

- Improve crate dependency graph

## Solution

- Invert a dependency

## Testing

- Tested ui and animation examples
2024-10-04 01:27:20 +00:00
Tim
20dbf790a6
Get rid of unnecessary mutable access in ui picking backend (#15630)
## Solution

Yeet

## Testing

Tested the `simple_picking` example
2024-10-03 21:30:52 +00:00
ickshonpe
9bb27e97c5
Fix entity leak in extract_uinode_borders (#15626)
# Objective

Fix for another leak, this time when extracting outlines.
2024-10-03 18:15:32 +00:00
rudderbucky
2da8d17a44
Add try_despawn methods to World/Commands (#15480)
# Objective

Fixes #14511.

`despawn` allows you to remove entities from the world. However, if the
entity does not exist, it emits a warning. This may not be intended
behavior for many users who have use cases where they need to call
`despawn` regardless of if the entity actually exists (see the issue),
or don't care in general if the entity already doesn't exist.

(Also trying to gauge interest on if this feature makes sense, I'd
personally love to have it, but I could see arguments that this might be
a footgun. Just trying to help here 😄 If there's no contention I could
also implement this for `despawn_recursive` and `despawn_descendants` in
the same PR)

## Solution

Add `try_despawn`, `try_despawn_recursive` and
`try_despawn_descendants`.

Modify `World::despawn_with_caller` to also take in a `warn` boolean
argument, which is then considered when logging the warning. Set
`log_warning` to `true` in the case of `despawn`, and `false` in the
case of `try_despawn`.

## Testing

Ran `cargo run -p ci` on macOS, it seemed fine.
2024-10-03 16:21:05 +00:00
Tim
461305b3d7
Revert "Have EntityCommands methods consume self for easier chaining" (#15523)
As discussed in #15521

- Partial revert of #14897, reverting the change to the methods to
consume `self`
- The `insert_if` method is kept

The migration guide of #14897 should be removed
Closes #15521

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-10-02 12:47:26 +00:00
Viktor Gustavsson
f86ee32576
Add UI GhostNode (#15341)
# 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>
2024-10-02 00:24:28 +00:00
UkoeHB
ead84e0e3d
Rename BreakLineOn to LineBreak (#15583)
# 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`.
2024-10-01 22:30:50 +00:00
ickshonpe
f53af2846c
Update the UI texture slice pipeline to work with the retained render world changes (#15578)
# Objective

Update the UI's texture slice extraction to work with the changes from
the retained render world PR (#15320).
2024-10-01 22:02:02 +00:00
Trashtalk217
56f8e526dd
The Cooler 'Retain Rendering World' (#15320)
- Adopted from #14449
- Still fixes #12144.

## Migration Guide

The retained render world is a complex change: migrating might take one
of a few different forms depending on the patterns you're using.

For every example, we specify in which world the code is run. Most of
the changes affect render world code, so for the average Bevy user who's
using Bevy's high-level rendering APIs, these changes are unlikely to
affect your code.

### Spawning entities in the render world

Previously, if you spawned an entity with `world.spawn(...)`,
`commands.spawn(...)` or some other method in the rendering world, it
would be despawned at the end of each frame. In 0.15, this is no longer
the case and so your old code could leak entities. This can be mitigated
by either re-architecting your code to no longer continuously spawn
entities (like you're used to in the main world), or by adding the
`bevy_render::world_sync::TemporaryRenderEntity` component to the entity
you're spawning. Entities tagged with `TemporaryRenderEntity` will be
removed at the end of each frame (like before).

### Extract components with `ExtractComponentPlugin`

```
// main world
app.add_plugins(ExtractComponentPlugin::<ComponentToExtract>::default());
```

`ExtractComponentPlugin` has been changed to only work with synced
entities. Entities are automatically synced if `ComponentToExtract` is
added to them. However, entities are not "unsynced" if any given
`ComponentToExtract` is removed, because an entity may have multiple
components to extract. This would cause the other components to no
longer get extracted because the entity is not synced.

So be careful when only removing extracted components from entities in
the render world, because it might leave an entity behind in the render
world. The solution here is to avoid only removing extracted components
and instead despawn the entire entity.

### Manual extraction using `Extract<Query<(Entity, ...)>>`

```rust
// in render world, inspired by bevy_pbr/src/cluster/mod.rs
pub fn extract_clusters(
    mut commands: Commands,
    views: Extract<Query<(Entity, &Clusters, &Camera)>>,
) {
    for (entity, clusters, camera) in &views {
        // some code
        commands.get_or_spawn(entity).insert(...);
    }
}
```
One of the primary consequences of the retained rendering world is that
there's no longer a one-to-one mapping from entity IDs in the main world
to entity IDs in the render world. Unlike in Bevy 0.14, Entity 42 in the
main world doesn't necessarily map to entity 42 in the render world.

Previous code which called `get_or_spawn(main_world_entity)` in the
render world (`Extract<Query<(Entity, ...)>>` returns main world
entities). Instead, you should use `&RenderEntity` and
`render_entity.id()` to get the correct entity in the render world. Note
that this entity does need to be synced first in order to have a
`RenderEntity`.

When performing manual abstraction, this won't happen automatically
(like with `ExtractComponentPlugin`) so add a `SyncToRenderWorld` marker
component to the entities you want to extract.

This results in the following code:
```rust
// in render world, inspired by bevy_pbr/src/cluster/mod.rs
pub fn extract_clusters(
    mut commands: Commands,
    views: Extract<Query<(&RenderEntity, &Clusters, &Camera)>>,
) {
    for (render_entity, clusters, camera) in &views {
        // some code
        commands.get_or_spawn(render_entity.id()).insert(...);
    }
}

// in main world, when spawning
world.spawn(Clusters::default(), Camera::default(), SyncToRenderWorld)
```

### Looking up `Entity` ids in the render world

As previously stated, there's now no correspondence between main world
and render world `Entity` identifiers.

Querying for `Entity` in the render world will return the `Entity` id in
the render world: query for `MainEntity` (and use its `id()` method) to
get the corresponding entity in the main world.

This is also a good way to tell the difference between synced and
unsynced entities in the render world, because unsynced entities won't
have a `MainEntity` component.

---------

Co-authored-by: re0312 <re0312@outlook.com>
Co-authored-by: re0312 <45868716+re0312@users.noreply.github.com>
Co-authored-by: Periwink <charlesbour@gmail.com>
Co-authored-by: Anselmo Sampietro <ans.samp@gmail.com>
Co-authored-by: Emerson Coskey <56370779+ecoskey@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com>
2024-09-30 18:51:43 +00:00
ickshonpe
c5742ff43e
Simplified ui_stack_system (#9889)
# 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>
2024-09-30 18:43:57 +00:00
MiniaczQ
5289e18e0b
System param validation for observers, system registry and run once (#15526)
# Objective

Fixes #15394

## Solution

Observers now validate params.

System registry has a new error variant for when system running fails
due to invalid parameters.

Run once now returns a `Result<Out, RunOnceError>` instead of `Out`.
This is more inline with system registry, which also returns a result.

I'll address warning messages in #15500.

## Testing

Added one test for each case.

---

## Migration Guide

- `RunSystemOnce::run_system_once` and
`RunSystemOnce::run_system_once_with` now return a `Result<Out>` instead
of just `Out`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2024-09-30 01:00:39 +00:00
charlotte
df23b937cc
Make CosmicFontSystem and SwashCache pub resources. (#15479)
# Objective

In nannou, we'd like to be able to access the [outline
commands](https://docs.rs/cosmic-text/latest/cosmic_text/struct.SwashCache.html#method.get_outline_commands)
from swash, while still benefit from Bevy's management of font assets.

## Solution

Make `CosmicFontSystem` and  `SwashCache` pub resources.

## Testing

Ran some examples.
2024-09-28 00:00:27 +00:00
Zachary Harrold
d70595b667
Add core and alloc over std Lints (#15281)
# 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>
2024-09-27 00:59:59 +00:00
Mohamed Osama
4e7801388c
Rename UiPickingBackend to UiPickingBackendPlugin (#15462)
solves #15450
2024-09-27 00:50:06 +00:00
ickshonpe
0fe33c3bba
use precomputed border values (#15163)
# 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>
2024-09-26 23:10:35 +00:00
Clar Fon
efda7f3f9c
Simpler lint fixes: makes ci lints work but disables a lint for now (#15376)
Takes the first two commits from #15375 and adds suggestions from this
comment:
https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366968300

See #15375 for more reasoning/motivation.

## Rebasing (rerunning)

```rust
git switch simpler-lint-fixes
git reset --hard main
cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate
cargo fmt --all
git add --update
git commit --message "rustfmt"
cargo clippy --workspace --all-targets --all-features --fix
cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate
cargo fmt --all
git add --update
git commit --message "clippy"
git cherry-pick e6c0b94f6795222310fb812fa5c4512661fc7887
```
2024-09-24 11:42:59 +00:00
Marco Buono
8e3db957c5
Add the ability to control font smoothing (#15368)
# 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()`
2024-09-23 17:28:25 +00:00
Piefayth
55dddaf72e
UI Scrolling (#15291)
# 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.
2024-09-23 17:17:58 +00:00
Benjamin Brienen
9386bd0114
feature gate picking backends (#15369)
# Objective

Fixes #15306

## Solution

- Add feature gate on the module and the place where each one is used
- Declare the features and make them default

## Testing

- CI
2024-09-22 19:35:15 +00:00
ickshonpe
48f2bd410b
RenderUiSystem::ExtractTextureSlice (#15332)
# Objective

Fixes #15330

## Solution
1. Add an `ExtractTextureSlice` variant to `RenderUiSystem`.
2. Add `RenderUiSystem::ExtractTextureSlice` to the `ExtractSchedule`
between `ExtractImages` and `ExtractBorders`.
3. Add `extract_ui_texture_slices` to the new `ExtractTextureSlice`
system set.

Which results in texture slice nodes being extracted before borders. No
more z-fighting, borders will always be drawn on top of texture-sliced
images.
2024-09-20 23:55:11 +00:00
Rich Churcher
fd329c0426
Allow to expect (adopted) (#15301)
# Objective

> Rust 1.81 released the #[expect(...)] attribute, which works like
#[allow(...)] but throws a warning if the lint isn't raised. This is
preferred to #[allow(...)] because it tells us when it can be removed.

- Adopts the parts of #15118 that are complete, and updates the branch
so it can be merged.
- There were a few conflicts, let me know if I misjudged any of 'em.

Alice's
[recommendation](https://github.com/bevyengine/bevy/issues/15059#issuecomment-2349263900)
seems well-taken, let's do this crate by crate now that @BD103 has done
the lion's share of this!

(Relates to, but doesn't yet completely finish #15059.)

Crates this _doesn't_ cover:

- bevy_input
- bevy_gilrs
- bevy_window
- bevy_winit
- bevy_state
- bevy_render
- bevy_picking
- bevy_core_pipeline
- bevy_sprite
- bevy_text
- bevy_pbr
- bevy_ui
- bevy_gltf
- bevy_gizmos
- bevy_dev_tools
- bevy_internal
- bevy_dylib

---------

Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
Co-authored-by: Antony <antony.m.3012@gmail.com>
2024-09-20 19:16:42 +00:00
patrickariel
3efef59d83
Enable/disable UI anti-aliasing (#15170)
# Objective

Currently, UI is always rendered with anti-aliasing. This makes bevy's
UI completely unsuitable for art-styles that demands hard pixelated
edges, such as retro-style games.

## Solution

Add a component for disabling anti-aliasing in UI.

## Testing

In
[`examples/ui/button.rs`](15e246eff8/examples/ui/button.rs),
add the component to the camera like this:

```rust
use bevy::{prelude::*, ui::prelude::*};

commands.spawn((Camera2dBundle::default(), UiAntiAlias::Off));
```

The rounded button will now render without anti-aliasing.

## Showcase

An example of a rounded UI node rendered without anti-aliasing, with and
without borders:


![image](https://github.com/user-attachments/assets/ea797e40-bdaa-4ede-a0d3-c9a7eab95b6e)
2024-09-16 23:06:23 +00:00
Blazepaws
cb6ab16c97
Reflect derived traits on all components and resources: bevy_ui (#15231)
Solves https://github.com/bevyengine/bevy/issues/15187 for bevy_ui
2024-09-15 17:52:38 +00:00
ickshonpe
1b1105e327
Remove border radius scaling (#15173)
# Objective

Fixes #15142

Split this off from #15163 as it's a very simple fix.

## Solution

UiScale was applied twice to border radius, remove the second
application.

## Testing

You can use this modified button example from the issue for testing:

```
use bevy::{color::palettes::basic::*, 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(UiScale(2.))
        .insert_resource(WinitSettings::desktop_app())
        .add_systems(Startup, setup)
        .add_systems(Update, button_system)
        .run();
}

const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);

fn button_system(
    mut interaction_query: Query<
        (
            &Interaction,
            &mut BackgroundColor,
            &mut BorderColor,
            &Children,
        ),
        (Changed<Interaction>, With<Button>),
    >,
    mut text_query: Query<&mut Text>,
) {
    for (interaction, mut color, mut border_color, children) in &mut interaction_query {
        let mut text = text_query.get_mut(children[0]).unwrap();
        match *interaction {
            Interaction::Pressed => {
                text.sections[0].value = "Press".to_string();
                *color = PRESSED_BUTTON.into();
                border_color.0 = RED.into();
            }
            Interaction::Hovered => {
                text.sections[0].value = "Hover".to_string();
                *color = HOVERED_BUTTON.into();
                border_color.0 = Color::WHITE;
            }
            Interaction::None => {
                text.sections[0].value = "Button".to_string();
                *color = NORMAL_BUTTON.into();
                border_color.0 = Color::BLACK;
            }
        }
    }
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    // ui camera
    commands.spawn(Camera2dBundle::default());
    commands
        .spawn(NodeBundle {
            style: Style {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                align_items: AlignItems::Center,
                justify_content: JustifyContent::Center,
                ..default()
            },
            ..default()
        })
        .with_children(|parent| {
            parent
                .spawn(ButtonBundle {
                    style: Style {
                        width: Val::Px(250.0),
                        height: Val::Px(100.0),
                        border: UiRect::all(Val::Px(25.0)),
                        // horizontally center child text
                        justify_content: JustifyContent::Center,
                        // vertically center child text
                        align_items: AlignItems::Center,
                        ..default()
                    },
                    border_color: BorderColor(Color::BLACK),
                    border_radius: BorderRadius::all(Val::Px(25.)),
                    background_color: NORMAL_BUTTON.into(),
                    ..default()
                })
                .with_child(TextBundle::from_section(
                    "Button",
                    TextStyle {
                        font: asset_server.load("fonts/FiraSans-Bold.ttf"),
                        font_size: 40.0,
                        color: Color::srgb(0.9, 0.9, 0.9),
                    },
                ));
            parent
                .spawn(NodeBundle {
                    style: Style {
                        width: Val::Px(150.0),
                        padding: UiRect::vertical(Val::Px(25.)),
                        height: Val::Px(100.0),
                        align_items: AlignItems::Stretch,
                        justify_content: JustifyContent::Stretch,
                        ..Default::default()
                    },
                    ..default()
                })
                .with_child(NodeBundle {
                    style: Style {
                        flex_basis: Val::Percent(100.),
                        ..Default::default()
                    },
                    background_color: RED.into(),
                    ..Default::default()
                });
        });
}
```

## Showcase

Using the modified button example

### main

<img alt="366023197-e6124f07-e522-4514-bd8e-7986ac32890c"
src="https://github.com/user-attachments/assets/b7b909ed-1184-4d9d-b50b-e30f4c1f76b2">


### this PR

![image](https://github.com/user-attachments/assets/89b89a2f-533f-41bd-b2cb-4743aec6519e)
2024-09-13 15:52:42 +00:00
ickshonpe
cacf3929db
fix spelling mistake (#15146)
# Objective

Fix spelling mistake: `interned_root_notes` -> `interned_root_nodes`
2024-09-10 20:21:40 +00:00
ickshonpe
8d143e3ed8
ui material node border calculations fix (#15119)
# Objective

Fixes  #15115

## Solution

Retrieve the size of the node's parent in a separate query and base
percentage border values on the parent node's width (or the width of the
viewport in the case of root nodes).
2024-09-09 22:35:29 +00:00
ickshonpe
4de9edeaa6
Retrieve the stack_index from Node in extract_ui_material_nodes instead of walking UiStack (#15104)
# Objective

 `ExtractedUiMaterialNode` is still walking the whole `UiStack`. 

more info: https://github.com/bevyengine/bevy/pull/9853

## Solution

Retrieve the `stack_index` from the `Node` component instead.
Also changed the `stack_index` field of `ExtractedUiMaterialNode` to
`u32`.
2024-09-09 16:18:37 +00:00
Marco Meijer
66b5128b6f
Add rect field to UI image (#15095)
# Objective

Fixes #14424 

## Solution

Add a rect field to UiImage, and update the extraction of ui images and
slices.

## Testing

I tested all possible combinations of having a rect, using a texture
atlas, setting image scale mode to sliced and image scale mode to tiled.
See the showcase section.

---

## Showcase

<img width="1279" alt="Screenshot 2024-09-08 at 16 23 05"
src="https://github.com/user-attachments/assets/183e53eb-f27c-4c8e-9fd5-4678825db3b6">

<details>
  <summary>Click to view showcase</summary>

```rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
        .add_systems(Startup, create_ui)
        .run();
}

fn create_ui(
    mut commands: Commands,
    assets: Res<AssetServer>,
    mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
) {
    let texture = assets.load("textures/fantasy_ui_borders/numbered_slices.png");
    let layout = TextureAtlasLayout::from_grid(UVec2::splat(16), 3, 3, None, None);
    let texture_atlas_layout = texture_atlas_layouts.add(layout);

    commands.spawn(Camera2dBundle::default());

    let style = Style {
        width: Val::Px(96.),
        height: Val::Px(96.),
        ..default()
    };

    commands
        .spawn(NodeBundle { ..default() })
        .with_children(|parent| {
            // nothing
            parent.spawn(ImageBundle {
                image: UiImage::new(texture.clone()),
                style: style.clone(),
                ..default()
            });

            // with rect
            parent.spawn(ImageBundle {
                image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)),
                style: style.clone(),
                ..default()
            });

            // with rect and texture atlas
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 8., 8.)),
                    style: style.clone(),
                    ..default()
                },
                TextureAtlas {
                    layout: texture_atlas_layout.clone(),
                    index: 1,
                },
            ));

            // with texture atlas
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()),
                    style: style.clone(),
                    ..default()
                },
                TextureAtlas {
                    layout: texture_atlas_layout.clone(),
                    index: 2,
                },
            ));

            // with texture slicer
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()),
                    style: style.clone(),
                    ..default()
                },
                ImageScaleMode::Sliced(TextureSlicer {
                    border: BorderRect::square(16.),
                    center_scale_mode: SliceScaleMode::Stretch,
                    sides_scale_mode: SliceScaleMode::Stretch,
                    max_corner_scale: 1.,
                }),
            ));

            // with rect and texture slicer
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)),
                    style: style.clone(),
                    ..default()
                },
                ImageScaleMode::Sliced(TextureSlicer {
                    border: BorderRect::square(2.),
                    center_scale_mode: SliceScaleMode::Stretch,
                    sides_scale_mode: SliceScaleMode::Stretch,
                    max_corner_scale: 1.,
                }),
            ));

            // with rect, texture atlas and texture slicer
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 8., 8.)),
                    style: style.clone(),
                    ..default()
                },
                TextureAtlas {
                    layout: texture_atlas_layout.clone(),
                    index: 1,
                },
                ImageScaleMode::Sliced(TextureSlicer {
                    border: BorderRect::square(1.),
                    center_scale_mode: SliceScaleMode::Stretch,
                    sides_scale_mode: SliceScaleMode::Stretch,
                    max_corner_scale: 1.,
                }),
            ));

            // with texture atlas and texture slicer
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()),
                    style: style.clone(),
                    ..default()
                },
                TextureAtlas {
                    layout: texture_atlas_layout.clone(),
                    index: 2,
                },
                ImageScaleMode::Sliced(TextureSlicer {
                    border: BorderRect::square(2.),
                    center_scale_mode: SliceScaleMode::Stretch,
                    sides_scale_mode: SliceScaleMode::Stretch,
                    max_corner_scale: 1.,
                }),
            ));

            // with tiled
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()),
                    style: style.clone(),
                    ..default()
                },
                ImageScaleMode::Tiled {
                    tile_x: true,
                    tile_y: true,
                    stretch_value: 1.,
                },
            ));

            // with rect and tiled
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)),
                    style: style.clone(),
                    ..default()
                },
                ImageScaleMode::Tiled {
                    tile_x: true,
                    tile_y: true,
                    stretch_value: 1.,
                },
            ));

            // with rect, texture atlas and tiled
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 8., 8.)),
                    style: style.clone(),
                    ..default()
                },
                TextureAtlas {
                    layout: texture_atlas_layout.clone(),
                    index: 1,
                },
                ImageScaleMode::Tiled {
                    tile_x: true,
                    tile_y: true,
                    stretch_value: 1.,
                },
            ));

            // with texture atlas and tiled
            parent.spawn((
                ImageBundle {
                    image: UiImage::new(texture.clone()),
                    style: style.clone(),
                    ..default()
                },
                TextureAtlas {
                    layout: texture_atlas_layout.clone(),
                    index: 2,
                },
                ImageScaleMode::Tiled {
                    tile_x: true,
                    tile_y: true,
                    stretch_value: 1.,
                },
            ));
        });
}
```

</details>
2024-09-09 16:16:33 +00:00
CrazyboyQCD
bca228fdaa
Simplify pick_rounded_rect (#15065)
# Objective

Simplify `pick_rounded_rect` with multiple `if` statements to make it
more readable and efficient([Godbolt
link](https://godbolt.org/z/W5vPEvT5c)).

Co-authored-by: WX\shixi <shixi1@cnwxsoft.com>
2024-09-09 15:40:00 +00:00
Ben Frankel
7b217a976c
Remove deprecated SpriteSheetBundle and AtlasImageBundle (#15062)
# Objective

Remove bundles that were deprecated in 0.14.

## Testing

`rg SpriteSheetBundle` and `rg AtlasImageBundle` show no results.
2024-09-09 15:36:09 +00:00
BD103
6ec6a55645
Unify crate-level preludes (#15080)
# Objective

- Crate-level prelude modules, such as `bevy_ecs::prelude`, are plagued
with inconsistency! Let's fix it!

## Solution

Format all preludes based on the following rules:

1. All preludes should have brief documentation in the format of:
   > The _name_ prelude.
   >
> This includes the most common types in this crate, re-exported for
your convenience.
2. All documentation should be outer, not inner. (`///` instead of
`//!`.)
3. No prelude modules should be annotated with `#[doc(hidden)]`. (Items
within them may, though I'm not sure why this was done.)

## Testing

- I manually searched for the term `mod prelude` and updated all
occurrences by hand. 🫠

---------

Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2024-09-08 17:10:57 +00:00
BigWingBeat
54aa45e62f
Fix bevy_ui compile error when bevy_picking feature is disabled (#15053)
# Objective

#14957 added the `pick_rounded_rect` function to `bevy_ui` in the
`picking_backend` module, which is gated behind the `bevy_picking`
feature. This function is used in that module, as well as in the `focus`
module. The latter usage is not gated behind the `bevy_picking` feature,
causing a compile error when the feature is disabled.

## Solution

Move the `pick_rounded_rect` function out of the `picking_backend`
module, as it does not depend on anything defined in that module. I put
it in `lib.rs` but it could reasonably be moved somewhere else instead.

## Testing

Encountered this compile error in a project and confirmed that this
patch fixes it.
2024-09-05 19:16:57 +00:00
ickshonpe
cb221d8852
Node::is_empty (#15050)
# Objective

Add a `Node::is_empty` method to replace the `uinode.size().x() <= 0. ||
uinode.size.y() <= 0.` checks.
2024-09-05 16:26:45 +00:00
ickshonpe
a0f5ea0d36
UI outlines radius (#15018)
# Objective

Fixes #13479

This also fixes the gaps you can sometimes observe in outlines
(screenshot from main, not this PR):

<img width="636" alt="outline-gaps"
src="https://github.com/user-attachments/assets/c11dae24-20f5-4aea-8ffc-1894ad2a2b79">

The outline around the last item in each section has vertical gaps. 

## Solution

Draw the outlines with corner radius using the existing border rendering
for uinodes. The outline radius is very simple to calculate. We just
take the computed border radius of the node, and if it's greater than
zero, add it to the distance from the edge of the node to the outer edge
of the node's outline.

---

## Showcase

<img width="634" alt="outlines-radius"
src="https://github.com/user-attachments/assets/1ecda26c-65c5-41ef-87e4-5d9171ddc3ae">

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2024-09-04 22:30:16 +00:00