This adds properties to the `FeathersColorSwatch` SceneComponent to
allow specifying a percentage of the swatch to remain opaque. This is
useful for color-pickers and generally handling colors with alpha
channels.
The property value will default to 0% so no migration guide is needed.
# Objective
- Allow a single `FeathersColorSwatch` to display the selected color
with and without its alpha channel.
## Solution
- Adds `FeathersColorSwatchProps` to allow dedicating a percentage of
the swatch area to the opaque color value
## Testing
Updated the `feathers_gallery` example to display the `rgba` color
swatch with 30% dedicated to the opaque color.
---
## Showcase
<img width="1280" height="720" alt="Screenshot_2026-06-15_21-09-53"
src="https://github.com/user-attachments/assets/1b3405f5-973b-42fb-abd5-b08c39c59cb6"
/>
# Objective
Add a way to change the justification for the text input in the
multiline_text_input example.
## Solution
Add a pop up menu that lets you set justify.
# Objective
Make the `multiple_text_inputs` example a bit more useful by giving each
text input a different justification, which should help with debugging
cursor behaviour.
## Solution
Add an input for each `Justify` variant along with a column on the left
labelling the justification it is using.
## Showcase
<img width="1798" height="750" alt="inputs-justified"
src="https://github.com/user-attachments/assets/324c1cd4-b6a1-4c0f-ad1a-38cd28867704"
/>
The IME support example configures its editable text field as a
single visible input, but the text layout still used default soft
wrapping. When the cursor moved through long text, wrapped visual lines
could appear as disconnected text segments.
Set the example input to use `TextLayout::no_wrap()`, matching the
other single-line text input examples while still allowing explicit
newlines.
Fixes https://github.com/bevyengine/bevy/issues/23932.
```sh
cargo check --example ime_support --features system_font_discovery
```
# Objective
- When `FontSource` resolution fails because an asset isn't yet loaded,
it doesn't attempt to reresolve the `FontSource` again the next frame.
- When a `Font` asset is removed, its font data is not unloaded from
Parley's font database
- The results from font lookups can be inconsistant when using `Handle`s
to identify a font.
Fixes#24356
## Solution
* In `load_font_assets_into_font_collection`, register each font twice,
once with an internal asset-specific alias for handle lookups and once
using its embedded family name.
* Use `set_changed` on `TextFont` to trigger chain detection, instead of
the `needs_rerender` flag on `TextBlock`. Schedule
`load_font_assets_into_collection` to run before
`detect_text_needs_rerender`, otherwise this would delay updates for a
frame.
* Store the changed family ids and asset paths, and set any `TextFont`s
that refer to them as changed.
* Don't update the measure funcs in `update_editable_text_content_size`
on font asset changes. Instead rely on the narrower `TextFont` change
detection, which is sufficient now because
`load_font_assets_into_font_collection` marks affected `TextFont`
components as changed when newly loaded font assets can affect font
resolution.
* Removed the mutable deref of `EditableText` at the start of
`update_editable_text_styles`. The editor is only mutable accessed if
updates to the styles need to be made.
* On unloading a font rebuild the whole font database, minus the
unloaded fonts, remap any generic families and relayout all text.
* Keep a copy of the registered generic families in `FontCx`, so they
can be remapped after unloading a font.
## Testing
```rust
use bevy::{
feathers::{controls::FeathersNumberInput, FeathersPlugins},
prelude::*,
};
fn main() {
App::new()
.add_plugins((DefaultPlugins, FeathersPlugins))
.add_systems(Startup, setup)
.add_systems(Update, spawn_number_input)
.run();
}
fn setup(mut commands: Commands) {
// ui camera
commands.spawn(Camera2d);
}
fn spawn_number_input(mut commands: Commands, mut is_spawned: Local<bool>) {
if *is_spawned {
return;
}
*is_spawned = true;
commands.spawn_scene(bsn! {
Node {
margin: UiRect::all(auto())
}
Outline
Children [
:FeathersNumberInput Node { width: px(200) }
]
});
}
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
`bsn!` currently requires surrounding macros in "field value position"
with `{}`:
```rust
bsn! {
Foo { value: {vec![ 100, 200 ]} }
}
```
This is annoying!
## Solution
Support macros in field-value-position:
```rust
bsn! {
Foo { value: vec![ 100, 200 ] }
}
```
## Testing
- Added a test that checks that this works
- Ported bsn! examples to use this
# Objective
Fix typos and other small issues in the documentation. I can drop the
changes to `bevy_reflect`'s and `bevy_anti_alias`'s crate descriptions
if it's a problem.
# Objective
Implement changes discussed with cart around erroring when using caching
syntax where its not implemented.
## Solution
Throw a compile error, in a way which is easy to remove once caching
exists/works for more cases.
Also contains a commit to alleviate trait errors which will (from my
experience writing this PR and fixing them in bevy) arise from fixing
this for existing bsn code. This commit can be dropped if not desired.
This would ideally be fixed upstream so this workaround can be removed:
https://github.com/rust-lang/rust/issues/141258#issuecomment-4565897810
Note: The error caused by this workaround is far from ideal. It
initially looks like a method call is missing, but ultimately the
hint/advice *is* correct
<img width="1982" height="477" alt="image"
src="https://github.com/user-attachments/assets/879f7630-ada8-4e63-9c59-80be466f37cf"
/>
## Testing
- `cargo test -p bevy_scene --lib`
- `cargo check`
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
Fix#19101. (Kinda, this is not the exact solution proposed in that
issue, but it still shortens most of the examples)
## Solution
Most of the images created in examples are for use in render targets.
`Image::new_target_texture` is made for exactly that and significantly
shortens the image creation process.
## Testing
I tested all the examples I changed and they seem to work fine.
---
btw for some reason most of the examples use
`TextureFormat::Bgra8UnormSrgb` while the documentation for
`Image::new_target_texture` recommends `TextureFormat::Rgba8UnormSrgb`
for SDR images. What's up with that?
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
Only select all on focus with `SelectAllOnFocus`.
It should be possible to have single line inputs without automatic
select all on focus.
## Solution
Remove the automatic select all on focus for single line inputs from
`on_focus_select_all`.
# Objective
Changes discussed with cart:
- `:` will not be called "inheritance" anymore, but instead "cacheable"
(name not final)
- the Template patching prefix changes `@` -> `~`
- the SceneComponent prefix changes `:` -> `@`
- the `:` prefix is allowed in front of `@SceneComponent` as well,
marking it as being cached
>> me: do we forbid caching of functions which take an argument?
> cart: We should forbid it until we support it, and I don't plan on
supporting this in the short term, as it adds new controversial design
aspects to the system (building a mapping from parameters to cached
scenes, ensuring the parameters are all hashable, etc).
Link to the discord thread, specifically my [recap
message](https://discord.com/channels/691052431525675048/1264881140007702558/1506422311056576702).
The exact reasoning is in the linked discord discussion, but the tldr
is:
"Inheritance" as a term for the ":" syntax didn't really fit what bsn
was doing, since scenes without `:` produce the same result. All `:` is,
is a way to say "cache this pls" with the design of bsn imposing the
limitation that only the first scene can be cached
That was all fine, except for one case: Disambiguating SceneComponents
from normal components. So if we redefine `:` to just mean "caching" we
needed to untangle it from SceneComponents. That means disambiguating
SceneComponents needed a different syntax, and the decision ended up
being to take the @ prefix from the *much* less common `@Template`
usecase, since `SceneComponent {@prop: val}` already used @ as well and
introduce a new prefix to replace `@Template` instead, replacing it with
`~Template`.
## Solution
Implement these changes in macros.
Notably, this *still* doesn't implement/enable caching right now.
Most docs are updated, except for the big blocks in
`bevy_scene/src/lib.rs` and `bevy_scene/macros/src/lib.rs` since i've
been working on reworking those on another branch and didn't want to
duplicate whats already been a lot of work and likely to be a bunch more
## Testing
- [x] `bevy_scene` tests passing
- [x] `cargo check --workspace` and `cargo check --workspace --examples`
passing
Since this PR does not yet enable caching, I think passing the current
tests should be enough.
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Allow UI elements to be positioned relative to the viewport rather than
their parent element.
Fixes#9564
## Solution
- New UI marker component `FixedNode`. Requires `Node` and
`OverrideClip`.
- `FixedNode` entities are treated as UI roots in layout, even if they
have a parent.
- `FixedNode`s don't inherit their parent's layout, clipping or
transform context.
- During the taffy layout updates children with `FixedNode` are skipped.
- Added a couple of basic helper functions to `UiSurface`, mainly there
to make the tests a little less painful.
- Added a fairly comprehensive range of new tests, including tests with
`GhostNode`s.
- In the Taffy layout (stored in `UiSurface`) there is nothing to
distinguish `FixedNode`s and root nodes, so they are treated identically
during updates.
--
The original suggestion was to implement it as a `PositionType::Fixed`
variant that could used with `Node`, but I think that would be much more
complicated without support from Taffy. Being able to just directly
query for and filter out `FixedNode` entities directly makes the
implementation much simpler and more efficient.
## Testing
Basic example which shows events bubbling up to the parent from the
fixed node:
```
cargo run --example fixed_node
```
There are also a number of new tests in the `layout` module.
```
cargo test -p bevy_ui --lib --features ghost_nodes
```
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Clean up the `text_input` example a bit.
## Solution
- Removed some unneeded style constraints.
- Disable newlines, disable wrapping, and set a responsive width for the
inputs.
- Submit on Enter, not Ctrl + Enter.
- Center the example using an auto margin.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
# Objective
Little bit of clean up for the `ime_support` example.
## Solution
* Use `row_gap` on the parent for spacing instead of setting individual
margins on the children.
* Give the text input node a fixed height and allow newlines.
## Testing
```
cargo run --example ime_support --features="system_font_discovery"
```
# Objective
In the `multiple_text_inputs` example, the input focus should move to
the next text input after the user submits the input's contents.
## Solution
- In `submit_text`, after submission, use the `TabNavigation` system
param to find the next input, and set it as the `InputFocus`.
- Submit on just `Enter`. The `Ctrl` + `Enter` chord should be reserved
for inputs that allow new lines.
See release notes for rationale and usage.
Fixes#17644, #20524
(It may not be a 100% fix, but it's good enough to close the ticket I
think.)
## Testing
- Manual testing with VoiceOver on MacOS (note that VoiceOver /
AccessKit does not work well without `desktop_app` being set, but this
causes other issues)
---------
Co-authored-by: Richard Braakman <rebraakman@gmail.com>
# Objective
Bevy currently supports OpenType `FontFeatures`, but it doesn't support
`FontVariations`, although `parley` has support for them.
Font features are mainly on/off values to vary the font (`u32`), while
font variations are a continuous range of values that can be set to vary
the font (`f32`).
I personally need it for setting `FILL` on the material design icon
font.
## Solution
I implemented `FontVariations` as a separate struct from `FontFeatures`
for now and copied it's api.
However, since both are so similar it's worth considering to merge them
into something like a `FontVariables` with a builder that has
`set_feature` and `set_variation` methods.
## Testing
Added a new example demonstrating how to set the font weight using
`FontVariations` instead of `FontWeight`.
(Setting font weight using `FontFeatures` does not work, although the
documentation suggests it should. I'm not sure if this depends on the
variable font used or whether this is an error in the documentation.)
## Showcase
<img width="946" height="541" alt="Screenshot 2026-05-02 225528"
src="https://github.com/user-attachments/assets/9067191b-8517-4fec-9283-45e57180658a"
/>
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Use our "shorthand" conventions for `Val` everywhere in feathers code /
usage
This also:
- adds a `VariantDefaults` derive to `Visibility` to make it usable
without `template_value`
- adds `From<Val>` to BorderRadius to support `px(10)` assignments
instead of `BorderRadius::all(px(10))`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
# Objective
Fix some scrolling jitters and bugs.
* The cursor can be scrolled out of view.
* Horizontal scrolling is jittery with a cursor taller then the line
height.
* Vertical scrolling jitters.
* Scrolling and cursor state should only update on changes to the editor
state and input focus (this is needed so scroll bars will work with a
text input).
Fixes#23931
## Solution
* Use the full cursor width when calculating the horizontal scroll
offset.
* Base vertical offsets on the bounds of the current visual line, not
the cursor.
* Update scrolling on `InputFocus` changes.
* `TextLayoutInfo` always holds the cursor geometry (if there is a
cursor), made the field a `(bool, Rect)` tuple, the bool is true if the
cursor should be rendered.
* Lots of clamping, logic behind it mostly trial and error tbh.
This should fix the major scrolling bugs, scrolling should feel
consistant and intuitive now.
Left look-ahead scrolling
https://github.com/bevyengine/bevy/issues/23933 left for a follow up,
after this is merged.
## Testing
```
cargo run --example multiple_text_inputs
cargo run --example multiline_text_input
cargo run --example ime_support --features="system_font_discovery"
```
With `ime_support` and `multiline_text_input`, you probably want to set
`TextLayout::new_with_no_wrap()` on the inputs to fully appreciate the
changes.
with naming conventions for other widgets.
# Objective
The `ToolButton` widget was not consistently named like the other
controls.
## Solution
Renamed.
## Testing
Basic manual testing.
@cart
# Objective
> With the introduction of scene components, the feathers API has now
reached its final form. This has a number of consequences:
> - There's no longer any reason to keep the experimental flag around
> - The effort of migrating the Bevy examples to feathers widgets is
unblocked, however I think we should start with migrating just one
example
- @viridia
## Solution
1. Rename the feature flag.
2. Write a migration guide.
This was previously attempted as part of
https://github.com/bevyengine/bevy/pull/22934, but I got pushback there.
Now that bsn! is more complete (scene components!) things are better.
I've opted not to change the default features here, even though it makes
it much harder to move our examples over, because that's a much more
contentious change. While I feel that Bevy's default features should be
example-oriented, that's still up for debate!
# Objective
Remove the `new_with_` prefixes from the `TextLayout` constuctor
functions. Generally, the "new" part is redundant and "with" is used by
fluent APIs.
## Solution
Just delete the prefixes, shorten the names (all on `TextLayout`).
* `new_with_justify` -> `justify`
* `new_with_linebreak` -> `linebreak`
* `new_with_no_wrap` -> `no_wrap`
# Objective
It is essentially a Bevy rite of passage to define a `Player` component
and then ask "ok when I spawn `Player`, how do I spawn all of the other
entities / components / scenes it needs to function with it?".
In Bevy, components are the "unit of behavior". The presence of a
component indicates that the entity should behave a certain way. Scenes
are _inextricable_ from behaviors, yet currently there is no way to tie
Components (bevy's unit of behavior) to scenes. _This is a fundamental
gap in the Bevy data model_. It is a problem that developers expect
their engine to solve for them.
It is high time that we had an official solution to this problem. BSN
was built to be that solution, and it is now time to do the last-mile
work to facilitate the `Component <-> Scene` relationship
## Solution
It is now possible to define "scene components", where `SceneComponent:
Component`:
```rust
#[derive(SceneComponent, Default, Clone)]
struct Player {
score: usize
}
impl Player {
fn scene() -> impl Scene {
bsn! {
Transform { translation: Vec3 { x: 10. } }
Children [
LeftHand,
RightHand,
]
}
}
}
```
This enables inheriting from the Player component as a scene:
```rust
world.spawn_scene(bsn! {
:Player { score: 0 }
})
```
After the inherited `Player` scene component is spawned, the _entire_
scene is available. This means that you can `Query<&Player>` in your ECS
systems, and _trust_ that the entire scene is also present.
### Inheritance Syntax vs Patch Syntax
Notice that this uses inheritance syntax in BSN (`:`), rather than
normal "component patch" syntax (ex: `bsn! { Player { score: 0 } }`.
Semantically these are different things:
- Scene inheritance syntax: constructs the full scene and inherits from
it
- Component patch syntax: _Just_ patches the component directly and
creates it if it doesn't exist. This will not do any scene inheritance.
You can still patch scene components this way as long as the scene
component is inherited somewhere "earlier" in the inheritance hierarchy.
Attempting to spawn a scene component on its own _without_ the scene
(ex: `commands.spawn(Player::default())`) will log an error, as Scene
Components should never exist without their scene.
### Custom Scene Functions
When deriving `SceneComponent`, it defaults to using `Self::scene` as
the "scene function". Scene functions can also be manually specified:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(player)]
struct Player {
score: usize
}
fn player() -> impl Scene {
bsn! { /* scene here */}
}
```
### Inherit BSN Asset Paths
Alternatively, a scene asset path can be specified:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene("player.bsn")]
struct Player {
score: usize
}
```
Once we port the glTF loader to BSN, this will also be supported:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene("player.gltf")]
struct Player {
score: usize
}
```
### Scene Props
Sometimes it is desirable to "parameterize" a scene: pass in values to
the scene which determine what the scene outputs are.
The answer to this in BSN is "scene props":
```rust
/// A UI widget that repeats "hello" text a given number of times.
#[derive(SceneComponent, Default, Clone)]
#[scene(HelloRepeaterProps)]
struct HelloRepeater;
#[derive(Default)]
struct HelloRepeaterProps {
repeat: usize,
}
impl HelloRepeater {
fn scene(props: HelloRepeaterProps) -> impl Scene {
let hellos = (0..props.repeat)
.map(|_| bsn!{ Text("hello") })
.collect::<Vec<_>>();
bsn! {
Node
Children [
{hellos}
]
}
}
}
world.spawn_scene(bsn! {
:HelloRepeater {
@repeat: 5
}
});
```
Notice the `@field` syntax, which specifies that a prop is being set
instead of a field. Props are evaluated "immediately" at the point of
inheritance where the scene is constructed. This means that they are not
"patchable".
You can set _both_ props and normal fields at the same time:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(WidgetProps)]
struct Widget {
value: usize
}
#[derive(Default)]
struct WidgetProps {
border: bool,
}
world.spawn_scene(bsn! {
:Widget {
@border: true,
value: 10,
}
});
```
Scene props can be used with templates:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(PlayerProps)]
struct Player;
#[derive(Default)]
struct PlayerProps {
head: AssetPath<'static>,
body: AssetPath<'static>,
}
impl Player {
fn scene(props: PlayerProps) -> impl Scene {
bsn! {
Transform
Visibility
Children [
(Head, Sprite { image: {props.head} }),
(Body, Sprite { image: {props.body} }),
]
}
}
}
world.spawn_scene(bsn! {
:Player {
@head: "big_head.png"
@body: "small_body.png"
}
});
```
### Scene Components are Template-able
```rust
#[derive(SceneComponent, FromTemplate)]
struct Player {
image: Handle<Image>,
}
impl Player {
fn scene() -> impl Scene {
bsn! { /* scene here */}
}
}
world.spawn_scene(bsn! {
:Player { image: "player.png" }
});
```
### The Scene Component is Always Added
Specifying the scene component manually in the scene function is not
necessary. It will be added automatically:
```rust
#[derive(SceneComponent, Default, Clone)]
struct Player {
score: usize
}
impl Player {
fn scene() -> impl Scene {
bsn! {
// No need to specify a Player component here.
// It is implied!
}
}
}
```
However you _can_ patch the scene component in the scene if you would
like. This comes in handy if you would like props to contribute to the
scene component's value:
```rust
impl Player {
fn scene(props: PlayerProps) -> impl Scene {
bsn! {
Player {
size_in_meters: {props.size_in_millimeters / 1000. }
}
}
}
}
```
### SceneComponent Trait
This is what SceneComponent looks like under the hood:
```rust
pub trait SceneConstructor: FromTemplate<Template: Default>
where
<Self::Template as Template>::Output: Component,
{
type Props: Default;
fn scene(props: Self::Props) -> impl Scene;
}
```
In general, developers should prefer `#[derive(SceneComponent)`, which
derives this trait automatically, ensures the Component is never spawned
without its scene, and automatically initializes the `Component` even if
it isn't specified explicitly in the user-defined scene function.
### Reusable Component Derive Logic
This PR moves the component derive logic into a new
`bevy_ecs_macro_logic` crate and refactors it to be reusable and
extendable.
This enables the `SceneComponent` derive to reuse and configure the
`Component` derive. I also took the liberty of porting the redundant
`Resource` derive logic to this system, as it follows the same pattern.
## PR Todo
- [ ] Port this PR description to docs on `Scene`.
## Whats Next
- Scene Component scene caching: It might make sense to cache
unparameterized scenes (like we do for scene assets) to cut down on
spawn costs. We could even have an opt-in parameterized scene cache (ex:
hash the props).
- Reactivity: It is important to note that props _are not reactive_.
They do not exist post-spawn (aka "at runtime") and they cannot be
changed. However they could easily be _part_ of a reactivity story:
- Props as components: insert props as components and re-run the scene
constructor when they are mutated
- Signal props: Reactive "signal" types could be passed down the init
hierarchy via props.
- Simpler "prop patching" codegen: currently for simplicity props reuse
the template-patching logic, but this introduces a few constraints and
complexities that are unnecessary. We should consider writing
specialized logic for them.
# Objective
The `feathers gallery` example logs "Disabled button clicked!" when its
"Primary" button is clicked.
## Solution
Log "Primary button clicked!" instead.
# Objective
Implement minimal multi-click support.
Add double click to select word and triple click to select all support
to text input widgets.
Fixes#23874
## Solution
New `MULTI_CLICK_DURATION` constant that sets the max time between
clicks for them to count as consecutive.
Added a field `count` to `Click`. 1 is a single click, 2 is a double
click, 3 is a triple click and so on. It's a `u8` and saturates at 255
if you click that many times.
Current multi-click state is stored in a field `clicking` on
`PointerButtonState`.
The `clicking` state is cleared after `MULTI_CLICK_DURATION` without
another click.
New `SelectWordAtPoint` `TextEdit`.
New observer system `on_pointer_click` in `bevy_ui_widgets::text_input`.
This queues `SelectWordAtPoint` for 2 clicks and `SelectAll` for 3 or
more. `Click` events are dispatched on release, so they happen after
`Press`. The `Press` for a double click might move the cursor but that
doesn't affect the `SelectWordAtPoint` edit that is dispatched.
#
The implementation is deliberately minimal, I just added here what was
needed for the text input. Left for followups:
- Tracking the initial click position and returning the displacement
with `Click`.
- Spatial tolerance (clicks are only multi-clicks if the pointer hasn't
moved too far).
- Configurable multi-click duration.
We could add a separate `MultiClick` event instead. It seems to me
things would be more complicated with both `Click` and `MultiClick`
observer systems though.
## Testing
Text inputs now support double click to select a word and triple click
to select all:
```
cargo run --example multiline_text_input
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Talin <viridia@gmail.com>
# Objective
- Users would like the ability to know how a focus was gained
## Solution
- Add `FocusCause` field to `FocusGained` event
- Automatically select all when navigating into a text input (even
without `SelectAllOnFocus`)
## Testing
- Tab into the color input of `feathers_gallery` to test that its text
gets highlighted
# Objective
- Text input is one of the headline feature in Bevy 0.19.
- Our defaults are currently very ugly, and shown across our user-facing
text examples.
- Good default matter for user perception of quality, and to make quick
prototyping easier.
- Additionally, the layout in the text_input example is erratic in a way
that looks poorly made.
Fixes#23955.
## Solution
1. Clean up the default colors for text input by picking boring,
uncontroversial tailwind colors.
2. Change the css::YELLOW borders in our text input examples to
something neutral that looks nice with our background.
3. Fix up the layout for the `text_input` example so it's both simpler
and looks much better.
## Testing
`cargo run --example text_input`
## Showcase
Before:
<img width="873" height="773" alt="image"
src="https://github.com/user-attachments/assets/bcc70bf6-e6b1-4bf4-b671-7924ac24e984"
/>
After:
<img width="757" height="274" alt="image"
src="https://github.com/user-attachments/assets/e1a3e325-b3a7-4a9b-831e-6886e6a818fc"
/>
# Objective
Add a platform-agnostic interface for interacting with the clipboard.
## Solution
New crate `bevy_clipboard` with a `ClipboardPlugin` that adds a
`Clipboard` resource. The clipboard is accessed using the methods
`fetch_text`, `fetch_image`, `set_text` and `set_image` on the
`Clipboard` resource. `fetch_text` returns a `ClipboardRead` with a
`poll_result` method that's used to get the actual value once it's
ready.
The `windows` and `unix` implementations both use the `arboard` crate.
On windows the `Clipboard` resource is a unit struct and a new arboard
clipboard instance is created and dropped for each clipboard access. On
unix targets the `Clipboard` resource holds a clipboard instance it
reuses each time. On both targets the `fetch_*` and `set_*` methods work
instantly.
On `wasm32` `Clipboard` is a unit struct. The `fetch_text` and
`set_text` functions spawn async tasks. The task spawned by `fetch_text`
updates the shared arc mutex option once the future is evaluated to get
the clipboard text. There is no image support on `wasm32`.
Everything seems to work but it feels like the design is a bit clumsy
and not very idiomatic. I don't tend to do much asynchronous
programming, maybe a reviewer can suggest an improved construction.
I also added an alternative `fetch_text_async` function for async access
that returns a `Result<String, ClipboardError>` future.
### Notes
* Doesn't support android targets yet.
* The wasm32 implementation doesn't support images. It's much more
complicated and probably best to left to a follow up.
## Testing
The PR includes a basic example `clipboard` that can be used for
testing.
The image display will only work if the image is already in the
clipboard before the example starts.
---------
Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
Co-authored-by: Andrew Zhurov <zhurov.andrew@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Adds the ability for picking backends to add custom hit data
- Fixes#16186
## Solution
- Adds an `extra` field to the HitData struct which can take any Data
that implements HitDataExtra. This is stored in an Arc which can be
downcast with a helper function in the system that consumes the hit.
- In the original ticket I suggested using a generic, however this
caused extensive code changes and complications down the line with the
HoverMap and OverMap so I decided against it.
## Testing
- I added an example custom_hit_data to test the new feature
- I tested all other picking examples to ensure they still work as
expected
- I tested the examples on Ubuntu
- I additionally tested the custom_hit_data example in wasm
---
## Showcase
<details>
<summary>Click to view showcase</summary>
### Creating custom hits:
```rust
let picks: Vec<(Entity, HitData)> = ray_cast
.cast_ray(ray, &settings)
.iter()
.map(|(entity, hit)| {
let extra = TriangleHitInfo {
triangle_vertices: hit.triangle,
};
let hit_data = HitData::new_with_extra(
ray_id.camera,
hit.distance,
Some(hit.point),
Some(hit.normal),
extra,
);
(*entity, hit_data)
})
.collect();
```
### Reading custom hits:
```rust
for hits in pointer_hits.read() {
for (_, hit) in &hits.picks {
let Some(info) = hit.extra_as::<TriangleHitInfo>() else {
continue;
};
let Some(vertices) = info.triangle_vertices else {
continue;
};
// do something cool with your custom hit data
}
}
```
### An example of what you can do with this
<img width="1291" height="730" alt="image"
src="https://github.com/user-attachments/assets/2d0e8aed-7059-470a-a0f6-1453356cfe6a"
/>
</details>
# Objective
This prefix was ruled unhelpful, and we previously removed it from most
of our types.
We missed a few apparently (spotted in #23924).
## Solution
Find and replace + update the migration guide.
# Objective
Part of #19236
## Solution
A numeric text input widget. This includes:
* Colored stripes ("sigils") for designating X, Y and Z input fields.
* Character filtering
* ValueChange events
Not supported:
* Validation error events
* Range and precision options
Additional changes in this PR:
* Added small labels (related to text inputs only insofar as numeric
inputs have small labels above them in the editor design).
* Refactored labels to be simple spans instead of nested entities - this
required a non-inherited text font theme component, so we renamed the
existing component to have the word "inherited" in the name.
* Consolidated the way focus outlines work for text inputs and other
widgets
* I had to make changes to Slider and other widgets to support the
`is_final` flag in `ValueChange`. This is necessary to allow listeners
the choice between spammy and not-spammy updates when listening to
widget outputs.
## Testing
Manual testing
## Showcase
<img width="353" height="101" alt="numeric_input"
src="https://github.com/user-attachments/assets/36efd243-fa01-484e-bab5-689aa6be4d9e"
/>
# Objective
IME support during text input is extremely important for non-Latin
languages: notably the CJK family (Chinese, Japanese, Korean). It would
be very nice to support it in our initial release!
Fixes#23795.
## Solution
1. Peek at the `input/text_input` example that @mockersf made and I
forgot about.
2. Steal its strategy and wire up the IME events to Bevy's proper text
input widgets.
3. Delete the now-obsolete example.
4. Add underlines to tentative characters (exposed by parley :D) so then
users can distinguish what they're typing from what they have typed.
5. Rename `editable_text` to the nicer `text_input`.
6. Create a dedicated example for `ime_support`, which uses system
fonts.
7. Add an `error_once!` to fix a footgun I tripped on when using system
fonts...
Per @mockersf's complaints, I've opted not to ship any new fonts in this
PR: the system fonts actually worked great!
## Testing
I've setup IME support on Windows, and added a small font with Japanese
support to the `editable_text` example.
It works quite well! We can even submit the values!
Known issues:
- when entering Japanese characters, the console is spammed with "ICU4X
data error: No segmentation model found for language: ja". I have no
idea where this is coming from: I think a system dependency is causing
this, and it's bubbled up by `parley`? IDK. Doesn't happen with Latin
characters, and it renders fine so...
-~~tab selecting the IME suggestion causes the selected text entry box
to change. I wasn't sure how we want to fix that, so Ieft it for now.~~
EDIT: fixed!
## Showcase
<img width="470" height="233" alt="image"
src="https://github.com/user-attachments/assets/8f8721b3-6746-46d5-8a20-40de634f52e5"
/>
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
# Objective
No helper nor example code exists for handling multiple radio button
groups. Currently if a user tries to use existing feathers_gallery code
to create multiple radio groups, radio buttons from all other groups
will become unchecked.
## Solution
- Within queries, only update the radio buttons inside their own group
- Add `radio_self_update()` convenience function to encapsulate this
desired behavior
## Testing
Manually tested inside feathers_gallery example
# Objective
Improved change detection for `EditableText`.
Fixes#23793
## Solution
* Remove the `text_edited` field from `EditableText` that was used for
manual change detection.
* Use the `PlainEditor`'s `Generation` to track changes.
* New component `EditableTextGeneration`. Newtypes `parley::Generation`.
Stores the generation from the last `TextLayoutInfo` update.
* If `EditableText::editor`'s and `EditableTextGeneration`'s generation
values aren't equal, reupdate `TextLayoutInfo`.
* Added support for `TextLayout::justify`.
* Split up `editable_text_system` into two systems
`update_editable_text_styles` and `update_editable_text_layout`. The
text input's style values need to be updated before layout, so the
measure func returns the correct size for the text layout.
* New `EditableText` `testbed_ui` scene.
* Added two numeric inputs to `multiline_text_input` that allow you to
set the height of the multiline input and its fontsize.
* Update selection rects on all changes, not just when a text input is
focused.
## Testing
```
cargo run --example multiline_text_input
```
The cursor appears to be missing for the numeric inputs in the example
but it isn't. The cursor gets clipped because it's at the end of the
right aligned input value text. If you press left it comes into view.
Cursor and scrolling behaviour needs some adjustments but that's out of
the scope of this PR.
# Objective
Spawning a scene on startup is a common pattern. Lets make it easier to
do so!
## Solution
- Add `SpawnSystem` and `SpawnListSystem` traits that are implemented
for functions that return scenes / scene lists, and return a system that
spawns the scene / handles errors.
### Before
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(world: &mut World) -> Result {
world.spawn_scene_list(bsn_list![Camera2d, ui()])?;
Ok(())
}
```
### After
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, scene.spawn())
.run();
}
fn scene() -> impl SceneList {
bsn_list![Camera2d, ui()]
}
```
This cuts out some boilerplate. It also further encourages people to
define standalone "scene functions" rather than embedding them in code,
which is generally a good pattern.
# Objective
* Bevy UI's accessibility module doesn't set the transform on
accessiblity nodes, ignoring scaling and rotation.
* UI accessibility nodes should be updated before `bevy_winit` updates
the accesskit adaptors, otherwise there will be a frames delay.
## Solution
Replaced the `calc_bounds` systems with a new system
`sync_bounds_and_transforms`.
Each accesskit `Node` corresponding to an `AccessibleNode` UI entity is
now given object-centered coordinates for its bounding rect (instead of
window coordinates) and a transform.
Accesskit uses local transforms so if an accessible node also has an
accessible parent, its transform has to be recomputed relative to its
parent.
## Testing
I modified the button example so that accesskit integration is enabled
by default and the button is drawn at a 45 degrees angle.
```
cargo run --example button
```
Screen readers should only react when the pointer is directly over the
rotated button if the changes are working.
The example changes should be reverted before merging.
# Objective
In the `feathers_gallery` example, on a `TextEditChange`
`handle_hex_color_change` updates `DemoWidgetStates` which causes
`update_colors` to update the hex input triggering another
`TextEditChange`.
## Solution
Only update `DemoWidgetStates` in `handle_hex_color_change` if the new
color is different.
# Objective
Part of #19236
The "disclosure toggle" is a small checkbox-like widget which displays a
chevron that toggles between pointing right and pointing down.
## Testing
Manual testing
# Objective
* Fix toggle behavior
* Add more style tokens and polish appearance
The original intention for the Feathers Checkbox was to toggle the
change immediately on mouse press, but currently only toggles on mouse
release, and lacks styling. Both behaviors are desirable depending on
context, such as being able to cancel an expensive checkmark click that
vastly changes render settings, so this pull request allows both, with
the mouse-press behavior enabled via the `ActivateOnPress` component.
In addition to adding styling, there will no longer a visible outline
for the checkbox in the checked state in the default theme. Having a
border and check symbol made it visually busy, especially at small
sizes.
---
## Showcase
### Before
<img width="161" height="83" alt="Screenshot 2026-04-15 at 7 58 09 PM"
src="https://github.com/user-attachments/assets/9cae7bcf-0b2a-46db-954a-b09332a7b06f"
/>
### After
<img width="161" height="108" alt="Screenshot 2026-04-15 at 8 34 57 PM"
src="https://github.com/user-attachments/assets/72d5083f-20db-45cd-8c1b-1ee711fcedf3"
/>
(Not pictured: new intermediate styling for mouse press)
# Objective
- In 0.18, we had 10 different functions that load assets (I'm not even
counting `load_folder`).
- In 0.19, we've even added `load_erased` - but it unfortunately doesn't
support all the features that the other variants support.
- We apparently needed `load_acquire_with_settings_override` which 1)
loads the asset, 2) uses the settings provided, 3) allows reading
unapproved asset paths, and 4) drops a guard once the load completes.
- That's fine if that's necessary. But we needed to create an explicit
variant for that.
- We need fewer load paths!
## Solution
- Create a builder.
- Store all these options dynamically instead of statically handling
each case.
- Have the caller choose a particular "kind" of load when they are
ready: `load`, `load_erased`, `load_untyped`, or `load_untyped_async`.
- I intentionally didn't provide a `load_async` or `load_erased_async`,
since those can be replicated using `load`/`load_erased` +
`AssetServer::wait_for_asset_id` to get the exact same effect.
I am also intentionally leaving `NestedLoader` untouched in this PR, but
a followup will duplicate this API for `NestedLoader`, which should make
it easier to understand.
Unlike the `NestedLoader` API, we aren't doing any type-state craziness,
so the docs are much more clear: users don't need to understand how
type-state stuff works, they just call the handful of methods on the
type. The "cost" here is we now need to be careful about including the
cross product of loads between static asset type, runtime asset type, or
dynamic asset type, crossed with deferred or async. In theory, if we
added more kinds on either side, we would need to expand this cross
product a lot. In practice though, it seems unlikely there will be any
more variants there. (maybe there could be a blocking variant? I don't
think this is a popular opinion though).
A big con here is some somewhat common calls are now more verbose.
Specifically, `asset_server.load_with_settings()` has become
`asset_server.load_builder().with_settings().load()`. I am not really
concerned about this though, since it really isn't that painful.
## Testing
- Tests all pass!
---
## Showcase
Now instead of:
```rust
asset_server.load_acquire_with_settings_override("some_path", |settings: &mut GltfLoaderSettings| { ... }, my_lock_guard);
```
You can instead do:
```rust
asset_server.load_builder()
.with_guard(my_lock_guard)
.with_settings(|settings: &mut GltfLoaderSettings| { ... })
.override_unapproved()
.load("some_path");
```
We also now cover more variants! For example, you can now load an asset
untyped with a guard, or with override_unapproved, etc.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Horizontal equivalent of the `visible_lines` option.
Allow users to set the width of a text input widget by number of glyphs.
Or for proportional fonts, where the glyph widths are non-uniform, it
would use a multiple of the advance width for "0".
## Solution
* New `visible_width: Option<f32>` field on `EditableText`.
* `TextInputMeasure` now has both a width and height field and both are
`Option<f32>`.
* `update_editable_text_content_size` calculates the width if
`visible_width` is `Some`.
* The width is calculated by multiplying the advance width of "0" by the
`visible_width` (if set) and the current scale factor.
## Testing
I altered the hex input in the `feathers` example to so it's sized to
fit 10 characters (includes space for the cursor).
```
cargo run --example feathers --features="experimental_bevy_feathers"
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>