Commit Graph

25 Commits

Author SHA1 Message Date
JoshValjosh
9cf1a1afa2
Remove unused query param (#18924)
# Objective

Was copying off `bevy_ui`'s homework writing a picking backend and
noticed the `Has<IsDefaultPickingCamera>` is not used anywhere.

## Testing

Ran a random example.

This shouldn't cause any behavioral changes at all because the
component/archetype access/filter flags should be the same. `Has<X>`
doesn't affect access since it doesn't actually read or write anything,
and it doesn't affect matched archetypes either. Can't think of another
reason any behavior would change.
2025-04-26 21:17:14 +00:00
Carter Anderson
e9a0ef49f9
Rename bevy_platform_support to bevy_platform (#18813)
# Objective

The goal of `bevy_platform_support` is to provide a set of platform
agnostic APIs, alongside platform-specific functionality. This is a high
traffic crate (providing things like HashMap and Instant). Especially in
light of https://github.com/bevyengine/bevy/discussions/18799, it
deserves a friendlier / shorter name.

Given that it hasn't had a full release yet, getting this change in
before Bevy 0.16 makes sense.

## Solution

- Rename `bevy_platform_support` to `bevy_platform`.
2025-04-11 23:13:28 +00:00
Antony
65e289f5bc
Unify picking backends (#17348)
# Objective

Currently, our picking backends are inconsistent:

- Mesh picking and sprite picking both have configurable opt in/out
behavior. UI picking does not.
- Sprite picking uses `SpritePickingCamera` and `Pickable` for control,
but mesh picking uses `RayCastPickable`.
- `MeshPickingPlugin` is not a part of `DefaultPlugins`.
`SpritePickingPlugin` and `UiPickingPlugin` are.

## Solution

- Add configurable opt in/out behavior to UI picking (defaults to opt
out).
- Replace `RayCastPickable` with `MeshPickingCamera` and `Pickable`.
- Remove `SpritePickingPlugin` and `UiPickingPlugin` from
`DefaultPlugins`.

## Testing

Ran some examples.

## Migration Guide

`UiPickingPlugin` and `SpritePickingPlugin` are no longer included in
`DefaultPlugins`. They must be explicitly added.

`RayCastPickable` has been replaced in favor of the `MeshPickingCamera`
and `Pickable` components. You should add them to cameras and entities,
respectively, if you have `MeshPickingSettings::require_markers` set to
`true`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-03-18 19:24:43 +00:00
Alice Cecile
2ad5908e58
Make Query::single (and friends) return a Result (#18082)
# Objective

As discussed in #14275, Bevy is currently too prone to panic, and makes
the easy / beginner-friendly way to do a large number of operations just
to panic on failure.

This is seriously frustrating in library code, but also slows down
development, as many of the `Query::single` panics can actually safely
be an early return (these panics are often due to a small ordering issue
or a change in game state.

More critically, in most "finished" products, panics are unacceptable:
any unexpected failures should be handled elsewhere. That's where the
new

With the advent of good system error handling, we can now remove this.

Note: I was instrumental in a) introducing this idea in the first place
and b) pushing to make the panicking variant the default. The
introduction of both `let else` statements in Rust and the fancy system
error handling work in 0.16 have changed my mind on the right balance
here.

## Solution

1. Make `Query::single` and `Query::single_mut` (and other random
related methods) return a `Result`.
2. Handle all of Bevy's internal usage of these APIs.
3. Deprecate `Query::get_single` and friends, since we've moved their
functionality to the nice names.
4. Add detailed advice on how to best handle these errors.

Generally I like the diff here, although `get_single().unwrap()` in
tests is a bit of a downgrade.

## Testing

I've done a global search for `.single` to track down any missed
deprecated usages.

As to whether or not all the migrations were successful, that's what CI
is for :)

## Future work

~~Rename `Query::get_single` and friends to `Query::single`!~~

~~I've opted not to do this in this PR, and smear it across two releases
in order to ease the migration. Successive deprecations are much easier
to manage than the semantics and types shifting under your feet.~~

Cart has convinced me to change my mind on this; see
https://github.com/bevyengine/bevy/pull/18082#discussion_r1974536085.

## Migration guide

`Query::single`, `Query::single_mut` and their `QueryState` equivalents
now return a `Result`. Generally, you'll want to:

1. Use Bevy 0.16's system error handling to return a `Result` using the
`?` operator.
2. Use a `let else Ok(data)` block to early return if it's an expected
failure.
3. Use `unwrap()` or `Ok` destructuring inside of tests.

The old `Query::get_single` (etc) methods which did this have been
deprecated.
2025-03-02 19:51:56 +00:00
AlephCubed
5f86668bbb
Renamed EventWriter::send methods to write. (#17977)
Fixes #17856.

## Migration Guide
- `EventWriter::send` has been renamed to `EventWriter::write`.
- `EventWriter::send_batch` has been renamed to
`EventWriter::write_batch`.
- `EventWriter::send_default` has been renamed to
`EventWriter::write_default`.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-02-23 21:18:52 +00:00
sam edelsten
7935c7e95f
Update picking docs to include position space (#17859)
# Objective

Add reference to reported position space in picking backend docs.

Fixes #17844 

## Solution

Add explanatory docs to the implementation notes of each picking
backend.

## Testing

`cargo r -p ci -- doc-check` & `cargo r -p ci -- lints`
2025-02-15 19:08:12 +00:00
sam edelsten
5eff6e80e1
Add relative position reporting to UI picking (#17681)
# Objective

Add position reporting to `HitData` sent from the UI picking backend.

## Solution

Add the computed normalized relative cursor position to `hit_data`
alongside the `Entity`.

The position reported in `HitData` is normalized relative to the node,
with `(0.,0.,0.)` at the top left and `(1., 1., 0.)` in the bottom
right. Coordinates are relative to the entire node, not just the visible
region.

`HitData` needs a `Vec3` so I just extended with 0.0. I considered
inserting the `depth` here but thought it would be redundant.

I also considered putting the screen space position in the `normal`
field of `HitData`, but that would require renaming of the field or a
separate data structure.

## Testing

Tested with mouse on X11 with entities that have `Node` components.

---

## Showcase

```rs
// Get click position relative to node
fn hit_position(trigger: Trigger<Pointer<Click>>) {
    let hit_pos = trigger.event.hit.position.expect("no position");
    info!("{}", hit_pos);
}
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-11 18:38:13 +00:00
ickshonpe
300fe4db4d
Store UI render target info locally per node (#17579)
# Objective

It's difficult to understand or make changes to the UI systems because
of how each system needs to individually track changes to scale factor,
windows and camera targets in local hashmaps, particularly for new
contributors. Any major change inevitably introduces new scale factor
bugs.

Instead of per-system resolution we can resolve the camera target info
for all UI nodes in a system at the start of `PostUpdate` and then store
it per-node in components that can be queried with change detection.

Fixes #17578
Fixes #15143

## Solution

Store the UI render target's data locally per node in a component that
is updated in `PostUpdate` before any other UI systems run.

This component can be then be queried with change detection so that UI
systems no longer need to have knowledge of cameras and windows and
don't require fragile custom change detection solutions using local
hashmaps.

## Showcase
Compare `measure_text_system` from main (which has a bug the causes it
to use the wrong scale factor when a node's camera target changes):
```
pub fn measure_text_system(
    mut scale_factors_buffer: Local<EntityHashMap<f32>>,
    mut last_scale_factors: Local<EntityHashMap<f32>>,
    fonts: Res<Assets<Font>>,
    camera_query: Query<(Entity, &Camera)>,
    default_ui_camera: DefaultUiCamera,
    ui_scale: Res<UiScale>,
    mut text_query: Query<
        (
            Entity,
            Ref<TextLayout>,
            &mut ContentSize,
            &mut TextNodeFlags,
            &mut ComputedTextBlock,
            Option<&UiTargetCamera>,
        ),
        With<Node>,
    >,
    mut text_reader: TextUiReader,
    mut text_pipeline: ResMut<TextPipeline>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    scale_factors_buffer.clear();

    let default_camera_entity = default_ui_camera.get();

    for (entity, block, content_size, text_flags, computed, maybe_camera) in &mut text_query {
        let Some(camera_entity) = maybe_camera
            .map(UiTargetCamera::entity)
            .or(default_camera_entity)
        else {
            continue;
        };
        let scale_factor = match scale_factors_buffer.entry(camera_entity) {
            Entry::Occupied(entry) => *entry.get(),
            Entry::Vacant(entry) => *entry.insert(
                camera_query
                    .get(camera_entity)
                    .ok()
                    .and_then(|(_, c)| c.target_scaling_factor())
                    .unwrap_or(1.0)
                    * ui_scale.0,
            ),
        };

        if last_scale_factors.get(&camera_entity) != Some(&scale_factor)
            || computed.needs_rerender()
            || text_flags.needs_measure_fn
            || content_size.is_added()
        {
            create_text_measure(
                entity,
                &fonts,
                scale_factor.into(),
                text_reader.iter(entity),
                block,
                &mut text_pipeline,
                content_size,
                text_flags,
                computed,
                &mut font_system,
            );
        }
    }
    core::mem::swap(&mut *last_scale_factors, &mut *scale_factors_buffer);
}
```

with `measure_text_system` from this PR (which always uses the correct
scale factor):
```
pub fn measure_text_system(
    fonts: Res<Assets<Font>>,
    mut text_query: Query<
        (
            Entity,
            Ref<TextLayout>,
            &mut ContentSize,
            &mut TextNodeFlags,
            &mut ComputedTextBlock,
            Ref<ComputedNodeTarget>,
        ),
        With<Node>,
    >,
    mut text_reader: TextUiReader,
    mut text_pipeline: ResMut<TextPipeline>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    for (entity, block, content_size, text_flags, computed, computed_target) in &mut text_query {
        // Note: the ComputedTextBlock::needs_rerender bool is cleared in create_text_measure().
        if computed_target.is_changed()
            || computed.needs_rerender()
            || text_flags.needs_measure_fn
            || content_size.is_added()
        {
            create_text_measure(
                entity,
                &fonts,
                computed_target.scale_factor.into(),
                text_reader.iter(entity),
                block,
                &mut text_pipeline,
                content_size,
                text_flags,
                computed,
                &mut font_system,
            );
        }
    }
}
```

## Testing

I removed an alarming number of tests from the `layout` module but they
were mostly to do with the deleted camera synchronisation logic. The
remaining tests should all pass now.

The most relevant examples are `multiple_windows` and `split_screen`,
the behaviour of both should be unchanged from main.

---------

Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-10 07:27:58 +00:00
Zachary Harrold
9bc0ae33c3
Move hashbrown and foldhash out of bevy_utils (#17460)
# Objective

- Contributes to #16877

## Solution

- Moved `hashbrown`, `foldhash`, and related types out of `bevy_utils`
and into `bevy_platform_support`
- Refactored the above to match the layout of these types in `std`.
- Updated crates as required.

## Testing

- CI

---

## Migration Guide

- The following items were moved out of `bevy_utils` and into
`bevy_platform_support::hash`:
  - `FixedState`
  - `DefaultHasher`
  - `RandomState`
  - `FixedHasher`
  - `Hashed`
  - `PassHash`
  - `PassHasher`
  - `NoOpHash`
- The following items were moved out of `bevy_utils` and into
`bevy_platform_support::collections`:
  - `HashMap`
  - `HashSet`
- `bevy_utils::hashbrown` has been removed. Instead, import from
`bevy_platform_support::collections` _or_ take a dependency on
`hashbrown` directly.
- `bevy_utils::Entry` has been removed. Instead, import from
`bevy_platform_support::collections::hash_map` or
`bevy_platform_support::collections::hash_set` as appropriate.
- All of the above equally apply to `bevy::utils` and
`bevy::platform_support`.

## Notes

- I left `PreHashMap`, `PreHashMapExt`, and `TypeIdMap` in `bevy_utils`
as they might be candidates for micro-crating. They can always be moved
into `bevy_platform_support` at a later date if desired.
2025-01-23 16:46:08 +00:00
ickshonpe
dd2d84b342
Remove ViewVisibility from UI nodes (#17405)
# Objective

The UI can only target a single view and doesn't support `RenderLayers`,
so there doesn't seem to be any need for UI nodes to require
`ViewVisibility` and `VisibilityClass`.

Fixes #17400

## Solution

Remove the `ViewVisibility` and `VisibilityClass` component requires
from `Node` and change the visibility queries to only query for
`InheritedVisibility`.

## Testing

```cargo run --example many_buttons --release --features "trace_tracy"```

Yellow is this PR, red is main.

`bevy_render::view::visibility::reset_view_visibility`
<img width="531" alt="reset-view" src="https://github.com/user-attachments/assets/a44b215d-96bf-43ec-8669-31530ff98eae" />

`bevy_render::view::visibility::check_visibility`
<img width="445" alt="view_visibility" src="https://github.com/user-attachments/assets/fa111757-da91-434d-88e4-80bdfa29374f" />
2025-01-23 05:26:10 +00:00
ickshonpe
adc33b5108
Rename TargetCamera to UiTargetCamera (#17403)
# Objective

It's not immediately obvious that `TargetCamera` only works with UI node
entities. It's natural to assume from looking at something like the
`multiple_windows` example that it will work with everything.

## Solution

Rename `TargetCamera` to `UiTargetCamera`.

## Migration Guide

`TargetCamera` has been renamed to `UiTargetCamera`.
2025-01-19 19:56:57 +00:00
Antony
02bb151889
Rename PickingBehavior to Pickable (#17266)
# Objective

PR #17225 allowed for sprite picking to be opt-in. After some
discussion, it was agreed that `PickingBehavior` should be used to
opt-in to sprite picking behavior for entities. This leads to
`PickingBehavior` having two purposes: mark an entity for use in a
backend, and describe how it should be picked. Discussion led to the
name `Pickable`making more sense (also: this is what the component was
named before upstreaming).

A follow-up pass will be made after this PR to unify backends.

## Solution

Replace all instances of `PickingBehavior` and `picking_behavior` with
`Pickable` and `pickable`, respectively.

## Testing

CI

## Migration Guide

Change all instances of `PickingBehavior` to `Pickable`.
2025-01-12 05:36:52 +00:00
MichiRecRoom
8b4c25ad5f
bevy_ui: Apply #![deny(clippy::allow_attributes, clippy::allow_attributes_without_reason)] (#17229)
# Objective
- https://github.com/bevyengine/bevy/issues/17111

## Solution
Set the `clippy::allow_attributes` and
`clippy::allow_attributes_without_reason` lints to `deny`, and bring
`bevy_ui` in line with the new restrictions.

## Testing
`cargo clippy --tests` and `cargo test --package bevy_ui` were run, and
no errors were encountered.
2025-01-08 19:30:19 +00:00
ickshonpe
d220eccbb1
More DefaultUiCamera fixes (#17120)
# Objective

Found more excessive `DefaultUiCamera` queries outside of extraction.
The default UI camera lookup only needs to be done once. Do it first,
not per node.

---------

Co-authored-by: MichiRecRoom <1008889+LikeLakers2@users.noreply.github.com>
2025-01-06 19:11:04 +00:00
Clar Fon
711246aa34
Update hashbrown to 0.15 (#15801)
Updating dependencies; adopted version of #15696. (Supercedes #15696.)

Long answer: hashbrown is no longer using ahash by default, meaning that
we can't use the default-hasher methods with ahasher. So, we have to use
the longer-winded versions instead. This takes the opportunity to also
switch our default hasher as well, but without actually enabling the
default-hasher feature for hashbrown, meaning that we'll be able to
change our hasher more easily at the cost of all of these method calls
being obnoxious forever.

One large change from 0.15 is that `insert_unique_unchecked` is now
`unsafe`, and for cases where unsafe code was denied at the crate level,
I replaced it with `insert`.

## Migration Guide

`bevy_utils` has updated its version of `hashbrown` to 0.15 and now
defaults to `foldhash` instead of `ahash`. This means that if you've
hard-coded your hasher to `bevy_utils::AHasher` or separately used the
`ahash` crate in your code, you may need to switch to `foldhash` to
ensure that everything works like it does in Bevy.
2024-12-10 19:45:50 +00:00
ickshonpe
8a3a8b5cfb
Only use physical coords internally in bevy_ui (#16375)
# 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>
2024-11-22 00:45:07 +00:00
BD103
7c593179e3
Fix bevy_picking plugin suffixes (#16082)
# Objective

- `MeshPickingBackend` and `SpritePickingBackend` do not have the
`Plugin` suffix
- `DefaultPickingPlugins` is masquerading as a `Plugin` when in reality
it should be a `PluginGroup`
- Fixes #16081.

## Solution

- Rename some structures:

|Original Name|New Name|
|-|-|
|`MeshPickingBackend`|`MeshPickingPlugin`|
|`MeshPickingBackendSettings`|`MeshPickingSettings`|
|`SpritePickingBackend`|`SpritePickingPlugin`|
|`UiPickingBackendPlugin`|`UiPickingPlugin`|

- Make `DefaultPickingPlugins` a `PluginGroup`.
- Because `DefaultPickingPlugins` is within the `DefaultPlugins` plugin
group, I also added support for nested plugin groups to the
`plugin_group!` macro.

## Testing

- I used ripgrep to ensure all references were properly renamed.
- For the `plugin_group!` macro, I used `cargo expand` to manually
inspect the expansion of `DefaultPlugins`.

---

## Migration Guide

> [!NOTE]
>
> All 3 of the changed structures were added after 0.14, so this does
not need to be included in the 0.14 to 0.15 migration guide.

- `MeshPickingBackend` is now named `MeshPickingPlugin`.
- `MeshPickingBackendSettings` is now named `MeshPickingSettings`.
- `SpritePickingBackend` is now named `SpritePickingPlugin`.
- `UiPickingBackendPlugin` is now named `UiPickingPlugin`.
- `DefaultPickingPlugins` is now a a `PluginGroup` instead of a
`Plugin`.
2024-10-25 20:11:51 +00:00
Carter Anderson
015f2c69ca
Merge Style properties into Node. Use ComputedNode for computed properties. (#15975)
# 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();
    }
}
```
2024-10-18 22:25:33 +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
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
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
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
4e9a62f094
Ignore clicks on uinodes outside of rounded corners (#14957)
# Objective

Fixes #14941

## Solution
1. Add a `resolved_border_radius` field to `Node` to hold the resolved
border radius values.
2. Remove the border radius calculations from the UI's extraction
functions.
4. Compute the border radius during UI relayouts in `ui_layout_system`
and store them in `Node`.
5. New `pick_rounded_rect` function based on the border radius SDF from
`ui.wgsl`.
6. Use `pick_rounded_rect` in `focus` and `picking_backend` to check if
the pointer is hovering UI nodes with rounded corners.
---

## Showcase

```
cargo run --example button
```


https://github.com/user-attachments/assets/ea951a64-17ef-455e-b5c9-a2e6f6360648

## Testing

Modified button example with buttons with different corner radius:

```
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(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,
                row_gap: Val::Px(10.),
                ..default()
            },
            ..default()
        })
        .with_children(|parent| {
            for border_radius in [
                BorderRadius {
                    top_left: Val::ZERO,
                    ..BorderRadius::MAX
                },
                BorderRadius {
                    top_right: Val::ZERO,
                    ..BorderRadius::MAX
                },
                BorderRadius {
                    bottom_right: Val::ZERO,
                    ..BorderRadius::MAX
                },
                BorderRadius {
                    bottom_left: Val::ZERO,
                    ..BorderRadius::MAX
                },
            ] {
                parent
                    .spawn(ButtonBundle {
                        style: Style {
                            width: Val::Px(150.0),
                            height: Val::Px(65.0),
                            border: UiRect::all(Val::Px(5.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,
                        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),
                        },
                    ));
            }
        });
}
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Matty <weatherleymatthew@gmail.com>
2024-09-03 12:38:59 +00:00
TotalKrill
6adf31babf
hooking up observers and clicking for ui node (#14695)
Makes the newly merged picking usable for UI elements. 

currently it both triggers the events, as well as sends them as throught
commands.trigger_targets. We should probably figure out if this is
needed for them all.

# Objective

Hooks up obserers and picking for a very simple example

## Solution

upstreamed the UI picking backend from bevy_mod_picking

## Testing

tested with the new example picking/simple_picking.rs


---

---------

Co-authored-by: Lixou <82600264+DasLixou@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2024-08-15 14:43:55 +00:00