Commit Graph

547 Commits

Author SHA1 Message Date
Jeb Brooks ea06f80230 Optionally allow FeathersColorSwatch to partially display opaque value (#24639)
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"
/>
2026-06-17 05:18:25 +00:00
ickshonpe 736d2b0a9e multiline_text_input example pop up justify menu (#24584)
# 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.
2026-06-11 03:36:18 +00:00
ickshonpe e52d2c5c53 Justify the inputs in the multiple_text_inputs example (#24583)
# 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"
/>
2026-06-10 17:57:58 +00:00
Talin 6588a8e7ef Modal dialog box for feathers (#24559)
# Objective

Part of #19236 

## Solution

Modal Dialog boxes

## Testing

Manual testing in feathers gallery

## Showcase

<img width="386" height="210" alt="feathers_dialog"
src="https://github.com/user-attachments/assets/9759db07-e1f6-4497-bc73-c8bb05798b1d"
/>
2026-06-09 00:51:04 +00:00
Talin 9a35b6a237 Feathers scrollbar and list view. (#24092)
# Objective

Part of #19236 

## Solution

Implementation of scrollbars and list views for Feathers.

## Testing

Manual testing:
* click to select
* wheel scrolling
* tab and keyboard navigation

## Showcase

<img width="380" height="182" alt="listbox"
src="https://github.com/user-attachments/assets/d3f902cd-0c73-475f-806b-d6ccb5ea3cac"
/>

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2026-06-07 02:10:38 +00:00
Jay f7f50d1a75 Fix IME text input wrapping (#23932) (#24421)
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
```
2026-06-06 18:29:22 +00:00
ickshonpe 457b91808a More consistant Font asset resolution and change detection (#24362)
# 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>
2026-06-04 04:55:04 +00:00
Carter Anderson c516bd8f48 bsn: support inline macros in field value position (#24524)
# 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
2026-06-04 00:28:58 +00:00
DavidCrossman 7c77ecd576 Fix documentation typos (#24446)
# 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.
2026-06-02 00:52:42 +00:00
laund 9ed66d7a06 bsn: throw error when using caching syntax on unsupported scene entries (#24473)
# 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>
2026-05-29 23:07:39 +00:00
Doonv 3f401d1507 Update examples to use Image::new_target_texture instead of manual image creation when possible (#23715)
# 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>
2026-05-22 14:33:16 +00:00
ickshonpe dbfa384182 Only select all on input focus with SelectAllOnFocus (#24278)
# 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`.
2026-05-22 10:40:46 +00:00
laund 5897ed605b BSN: changing prefixes for Template and SceneComponent to allow removing confusing "inheritance" concept (#24367)
# 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>
2026-05-21 15:37:35 +00:00
ickshonpe 6dca76afcf FixedNode (#24323)
# 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>
2026-05-21 11:08:44 +00:00
ickshonpe a382a230a2 multiple_text_input example layout fixes (#24294)
# Objective

These are meant to be single line inputs, but:

<img width="1552" height="954" alt="image"
src="https://github.com/user-attachments/assets/5e7ab2c7-9bb2-46cd-9a42-bca36b933837"
/>

A mess!

## Solution

1. Add `TextLayout::no_wrap()` to each input and their outputs.
2. Add an intermediate node wrapping each output node applying clipping
to hide the overflow.

<img width="1608" height="489" alt="fixed-inputs"
src="https://github.com/user-attachments/assets/22dc436c-e677-4b1f-a569-d3a9c9effc1c"
/>

## Testing

```
cargo run --example multiple_text_inputs  --features="system_clipboard"
```
2026-05-21 10:16:40 +00:00
ickshonpe 7500b6c5aa text_input example clean up (#24297)
# 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>
2026-05-21 10:16:23 +00:00
ickshonpe 7af4723083 ime_support example clean up (#24296)
# 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"
```
2026-05-21 10:13:03 +00:00
ickshonpe 10db67e459 multiple_text_inputs example: Navigate to next input on submission (#24295)
# 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.
2026-05-21 09:26:17 +00:00
ickshonpe bf9a118c46 text input selection radius (#24307)
# Objective

Allow rounded corners for text input selection regions.

Fixes #23952

## Solution

* Added a `selection_radius` field to `TextCursorStyle`, defaults to
`0.`.
* In `extract_text_cursor` set a corner radius for each selection rect,
after clamping for short and inner corners.

---

Left rounded inner corners for a follow up, as they are significantly
more complicated to implement.

## Testing

Added an option to set the selection radius to the
`multiline_text_input` example:

```
cargo run --example multiline_text_input --features="system_clipboard"
```

<img width="400" alt="image"
src="https://github.com/user-attachments/assets/a10bb552-ac03-4e06-a287-53c57e5c2f45"
/>

## Showcase

<img width="400" alt="rounded"
src="https://github.com/user-attachments/assets/79d59c66-7fb0-4ad4-8a17-308c755c1f23"
/>
<img width="400" alt="r2"
src="https://github.com/user-attachments/assets/a3dfd8a6-f85f-4534-ba61-4a8c74e9793d"
/>
<img width="400" alt="r3"
src="https://github.com/user-attachments/assets/78e4d863-198e-4ee0-ad33-22e7cde7016a"
/>
2026-05-21 08:24:38 +00:00
Talin 8b67e34536 Introduce AccessibleLabel component. (#24308)
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>
2026-05-21 07:56:13 +00:00
Lennard d2481ccf60 Add OpenType font variations (#24088)
# 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>
2026-05-08 13:29:06 +00:00
Carter Anderson abc6c9049b Feathers bsn! style improvements (#24063)
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>
2026-05-04 03:11:02 +00:00
ickshonpe 211b9ab63f EditableText scrolling and cursor fixes (#24032)
# 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.
2026-05-03 23:55:52 +00:00
Talin d03f0c7dc1 Renamed ToolButton to FeathersToolButton to be consistent (#24098)
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
2026-05-03 23:54:17 +00:00
Alice Cecile ba7b3c8950 Rename bevy_experimental_feathers feature to bevy_feathers (#24108)
# 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!
2026-05-03 23:53:51 +00:00
ickshonpe b00ff93c87 Remove new_with_ prefix from the TextLayout constuctor functions (#24049)
# 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`
2026-05-01 21:08:18 +00:00
Carter Anderson aadbf46184 Scene Components (#24008)
# 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.
2026-05-01 21:07:22 +00:00
ickshonpe 1ed78a2a52 Fix feathers gallery button info (#24062)
# Objective

The `feathers gallery` example logs "Disabled button clicked!" when its
"Primary" button is clicked.

## Solution

Log "Primary button clicked!" instead.
2026-05-01 21:02:34 +00:00
ickshonpe 68c4223091 multi-click support (#24023)
# 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>
2026-04-29 15:59:37 +00:00
Jordan Halase 1dac2eca81 Add cause field to FocusGained event (#23993)
# 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
2026-04-26 23:08:46 +00:00
Alice Cecile 90b41543e2 Improve default colors for text_input widgets (#23960)
# 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"
/>
2026-04-24 05:21:39 +00:00
ickshonpe 41f170c0a3 Basic clipboard support (#19106)
# 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>
2026-04-23 21:18:07 +00:00
Martin Edlund c3c118c20c Add support for custom picking data (#23245)
# 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>
2026-04-23 16:18:18 +00:00
Alice Cecile 8623d29a7a Remove Core prefix from remaining UI widget components (#23938)
# 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.
2026-04-22 19:56:27 +00:00
Talin 64fe756adb Number input (#23842)
# 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"
/>
2026-04-21 05:47:05 +00:00
Alice Cecile 61042b10f0 IME support for text input (#23841)
# 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>
2026-04-21 04:47:51 +00:00
Jordan Halase 9eed7a39a1 Fix Feathers radio groups, add radio_self_update() (#23890)
# 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
2026-04-20 05:08:41 +00:00
ickshonpe 8b8a432ab4 EditableText change detection using parley::Generation (#23785)
# 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.
2026-04-19 18:38:47 +00:00
Carter Anderson aa5322ed0f BSN: scene.spawn() system ergonomics (#23868)
# 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.
2026-04-19 17:25:21 +00:00
Jordan Halase 6af4a0329c Fix toggle behavior and add styling to Feathers Radio Buttons (#23869)
# Objective

Continuation of https://github.com/bevyengine/bevy/pull/23820 and
https://github.com/bevyengine/bevy/pull/23830

This fixes the clicking behavior to toggle on mouse-release, or on
mouse-press with the `ActivateOnPress` component. More styling tokens
are also added for transition states.

---

## Showcase

### Before

<img width="91" height="91" alt="Screenshot 2026-04-17 at 11 32 39 PM"
src="https://github.com/user-attachments/assets/b1b8832d-7aaa-45ee-a641-4c100d44a232"
/>

### After

<img width="91" height="91" alt="Screenshot 2026-04-17 at 11 13 33 PM"
src="https://github.com/user-attachments/assets/d3cf5bf6-1dfe-4d88-8dd1-fc719449e7b5"
/>

(Not pictured: new intermediate styling for mouse press)

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2026-04-19 15:56:24 +00:00
Rob Parrett d4379d437d Revert unintended example changes (#23877)
# Objective

Fixes #23873

## Solution

Partial git revert

## Testing

I did not test
2026-04-18 17:19:09 +00:00
ickshonpe bb1d51623b Fix UI accessibility transforms and update ordering (#23859)
# 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.
2026-04-18 04:35:53 +00:00
ickshonpe 4c7e99cc56 feathers_gallery infinite update loop fix (#23827)
# 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.
2026-04-17 15:57:02 +00:00
Jordan Halase 6c867fa44a Fix and Polish Feathers Toggle Switch (#23830)
# Objective

This is a continuation of https://github.com/bevyengine/bevy/pull/23820
for toggle switches to fix their clicking behavior and add more style
tokens. Again, adding the `ActivateOnPress` component will change its
state instantly on mouse-press rather than mouse-release.

Styling is also changed for visibility and disambiguation.

---

## Showcase

### Before

<img width="120" height="31" alt="Screenshot 2026-04-16 at 1 53 12 PM"
src="https://github.com/user-attachments/assets/22fd0374-76cb-40bb-9428-cbd7c69f2e50"
/>

### After

<img width="159" height="31" alt="Screenshot 2026-04-16 at 3 32 21 PM"
src="https://github.com/user-attachments/assets/ecb5bee6-0dbd-4598-8487-cdb333575f78"
/>

(Not pictured: new intermediate styling for mouse press)
2026-04-17 02:37:18 +00:00
Talin 6b4a003428 Disclosure toggle widget. (#23817)
# 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
2026-04-16 04:39:26 +00:00
Jordan Halase e677babe59 Fix and Polish Feathers Checkbox (#23820)
# 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)
2026-04-16 02:12:07 +00:00
andriyDev 61127f6d01 Replace all different load variants in AssetServer with a builder. (#23663)
# 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>
2026-04-15 18:32:25 +00:00
ickshonpe 250a7c0e50 EditableText::visible_width (#23717)
# 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>
2026-04-15 17:02:41 +00:00
Talin 574e9356cb Decorative widgets (#23804)
# Objective

Part of #19236 

## Solution

A bunch of new, decorative widgets - see release note.

## Testing

Manual testing

## Showcase

<img width="384" height="207" alt="panes"
src="https://github.com/user-attachments/assets/0b0bb2ee-d520-4280-938e-e08d5c4e49d1"
/>

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-15 15:42:55 +00:00
Talin 4553e66916 Add menu_divider widget. (#23788)
# Objective

Menu dividers have been requested by the editor ui design folks.

## Solution

A new `menu_divider` bsn function

## Testing

Manual testing

## Showcase

<img width="103" height="120" alt="menu_divider"
src="https://github.com/user-attachments/assets/382eb57b-732d-454b-a0df-96ebcc39df97"
/>
2026-04-14 03:13:49 +00:00