Skip to content

Add scrubbing / dragging to number_input widget.#24636

Open
viridia wants to merge 7 commits into
bevyengine:mainfrom
viridia:number_scrubbing
Open

Add scrubbing / dragging to number_input widget.#24636
viridia wants to merge 7 commits into
bevyengine:mainfrom
viridia:number_scrubbing

Conversation

@viridia

@viridia viridia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Objective

Part of #19236

This PR is a near-total rewrite of the feathers number input widget. This version adds the ability to edit the value by dragging, behaviorally similar to the widgets in Blender.

Features

  • Click to edit textually
  • Drag to adjust
  • Adaptive drag speed heuristic
  • Conditional slider bar (only appears if soft limits are specified)
  • Soft and hard limits (ref. Blender documentation)
  • Precision and step options
  • Cursor shape changing
  • Support for different number formats: f32, f64, i32, i64

Backwards compatibility

The method for programmatically updating the edited value has changed to be more consistent with other widgets - instead of sending an event, you now insert a NumberInputValue component with the new value.

Testing

  • extensive manual testing via a new example, feathers_number_input.

Additional Information

Things left to do:

  • Visible increment / decrement arrows (only for fields with no soft limit)
  • Support for unit conversions and units display (meters, centimeters, degrees, etc.)
  • The theme colors are not final; there's some debate about whether we will need to implement multiple thematic variations of this widget in order to maintain sufficient contrast against different backgrounds.
  • Accessibility support needs work.
  • The example has some placeholder labels that we supposed to mirror the input value, but that has not been finished.
  • Does not enable drag capturing in CursorOption because bevy_picking doesn't work with captured pointers.

Note: because of the complexity of this widget, it does not use the "slider" headless widget, and instead is based on the EditableText component, with slider-like behaviors layered on top.

Also, this widget pretty much subsumes all of the functionality of the feathers slider, making the latter unnecessary. The only advantage of FeathersSlider is that, since it only supports f32, it has a simpler API.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-UI Graphical user interfaces, styles, layouts, and widgets D-Complex Quite challenging from either a design or technical perspective. Ask for help! M-Release-Note Work that should be called out in the blog due to impact X-Contentious There are nontrivial implications that should be thought through S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 15, 2026
@alice-i-cecile alice-i-cecile added this to the 0.20 milestone Jun 15, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in UI Jun 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note.

Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes.

Comment on lines +83 to +86
demo_field_f32("soft limit + disabled", 2.0, bsn!(
InteractionDisabled
template_value(SoftLimit(NumberInputRange::F32(0.0..10.0)))
)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The disabled input can still be edited:

Image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't quite absorbed the design but it looks like it's the scrubber_on_release function setting input focus without checking for Has<InteractionDisabled>.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK so I added code to check the disabled state on pointer release. This fixes the problem you pointed out, but it's not complete: you can still set focus to the input widget via tab navigation.

What's probably going to have to happen is that the add/remove observers for InteractionDisabled will need to put the editable text into a read-only mode. Unfortunately, such a mode does not currently exist, we'll need to add it. Changing the widget into a static text entity is a non-starter because the layout and scrolling won't match.

Note that I have similar issues with accessibility: the a11y system requires that the accessibility node component be placed on the entity that has focus. However, for this widget, the focusable entity is a private entity internal to the widget. If the user inserts an AccessibleLabel on the root, it won't work, and BSN has no mechanism for inserting components on internal child entities.

* Use an alpha-blended background color to permit input fields to look good against various backgrounds.

@jbuehler23 jbuehler23 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in jackdaw, I treat the "label"/"button" section as the draggable section so as to avoid confusion with the actual text input section (see where my mouse in pointed):

Image

This for me is more of a personal preference, as I know there are other widgets where you drag the text input field - I believe this is the case in ImGui. What are you thoughts on this?

@viridia

viridia commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@jbuehler23 The label is optional. In the figma mocks, many of the input fields don't have labels: https://www.figma.com/design/fkYfFPSBgnGkhbQd3HOMsL/Bevy-Editor?node-id=90-2&p=f

@jbuehler23

jbuehler23 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@jbuehler23 The label is optional. In the figma mocks, many of the input fields don't have labels: https://www.figma.com/design/fkYfFPSBgnGkhbQd3HOMsL/Bevy-Editor?node-id=90-2&p=f

Oh i didn't realize all of those text inputs should draggable 😅

My personal view is that this is better than not having a draggable text input widget, so let's merge!

Obviously anything outside could go in a follow up

Comment thread crates/bevy_feathers/src/controls/number_input.rs Outdated
Comment thread examples/ui/widgets/feathers_gallery.rs
Comment thread examples/ui/widgets/feathers_gallery.rs Outdated

@ickshonpe ickshonpe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the numeric input's EditableText gains input focus it switches to an I-beam cursor, but if I move the cursor away and then reenter the node, it switches back to a resize cursor, even though EditableText is still the focus.

Comment on lines +909 to +910
// TODO: Can simplify this by only changing the window that this ui element is on,
// but I've forgotten how to get the window from a computed node.

@ickshonpe ickshonpe Jun 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get the camera entity from the ComputedUiTargetCamera component, then the camera's RenderTarget component has the window ref.

&mut commands,
);

// Only hide the cursor if there's no slide bar.

@ickshonpe ickshonpe Jun 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intuition is that if the cursor is hidden like this, then its position should be locked in place until the button is released.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All right, I removed this.

Unfortunately, the CursorOptions drag capturing feature does not work well with bevy_picking right now. Once you capture the pointer, you no longer get picking events; instead you have to process the global pointer events upstream from the picking framework, something which is hard for a widget to do.

@viridia viridia Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, my logic was this: when dragging, depending on where you initially clicked, the mouse pointer can obscure the digits and make them hard to read. This isn't a huge issue, since you can, while dragging, move the mouse down and out of the way (only horizontal movement counts for changing the value) - but it's an extra step the user has to take.

Note that when a soft limit is present, a slider bar appears, and this makes it much easier to see the current value since the pointer is much smaller than the bar graphic. So it's not necessary to hide the cursor in that case.

Another option would be to come up with a custom cursor that is translucent or otherwise has a design which doesn't obscure the digits as much.

@ickshonpe ickshonpe Jun 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I think disappearing the cursor is the correct behaviour, it's just that it's going to be surprising to users if the cursor is not locked.

It doesn't matter where the cursor goes as long as it's not visible, so could we just reset the cursor's position when it's released back to the drag's origin point?

Comment thread crates/bevy_feathers/src/controls/number_input.rs Outdated
Comment thread crates/bevy_feathers/src/controls/number_input.rs
Comment thread crates/bevy_feathers/src/controls/number_input.rs
viridia and others added 2 commits June 17, 2026 08:08
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
@viridia

viridia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Also note I haven't done a release note or migration guide yet, I'll probably do those in one of several follow-up PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! M-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Contentious There are nontrivial implications that should be thought through

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

4 participants