Skip to content

Dependency coupling between widgets and input focus #19057

@alice-i-cecile

Description

@alice-i-cecile

Discussed in #19049

Originally posted by viridia May 3, 2025
@ickshonpe recently implemented a text input widget that works with Bevy's cosmic-text integration. I would like to see this work upstreamed: I see it as an important part of the "core widgets" collection. However, there are some open questions.

Input Focus Integration

One issue is how the input widget should depend on the input focus crate.

Normally, when you have multiple input fields, input focus determines which input field is active and accepting input. Focusing a widget affects it in two ways: it causes input events to be routed to that widget, and it causes the cursor/selection highlight to be shown.

For the first of these two effects (routing input events), the actual widget does not need a direct dependency on the input focus crate, because the routing is handled by the focus framework. However, if the focus framework is absent (because the plugin has not been initialized) then the widget will not get any events and will not work.

For the second effect - showing and hiding the selection highlight - the widget needs to examine the InputFocus resource to see whether it has focus. This requires a direct dependency.

The issue that was brought up on Discord is that our input focus plugin is not enabled by default. This means that a text input widget that has a dependency on the InputFocus resource can't function unless the plugin is enabled.

There is a reason why input focus is not enabled by default. In addition to registering the resources (InputFocus and InputFocusVisible), the plugin also enables bubbling of input events. Without the plugin, input events are just global events in an event queue. Since bubbling has a small but non-zero cost, some games might not want to enable this. Particularly, many games don't use input focus: instead, keyboard and gamepad events are directly mapped to game actions, there are no "widgets".

Alternatively, if there is only one widget on the screen (such as "enter character name") there is also no need for input focus - you can just route keyboard events to the widget unconditionally via an event reader system.

The question is whether the text input widget can be decoupled from input focus. There are several possible approaches:

Option 1: split the plugin. Currently the input focus plugin has two jobs: registering the resources, and enabling bubbling of input events. We could divide this into two separate plugins, so that the resources were always registered by default (since the costs are small), but bubbling would be optional.

Note that with this option, a user who wants to use text input, but doesn't want to enable tab navigation behavior, would be required to manually set the InputFocus resource to the entity id of the text widget so that selection highlights are shown.

Option 2: we could make a "headless" version of the text input widget. The philosophy of core widgets is that core widgets are agnostic with regards to appearance: it's up to the user to decide how they want to display things. The other core widgets like button and checkbox don't directly depend on input focus, because the focus highlight is purely a visual effect and doesn't impact the widget's behavior. Instead, it's the responsibility of the styled/opinionated widget that is built on the foundation of the core widget to display focus highlights, which means the styled widget would need to have a reference to focus - but that styled widget is not built into Bevy so we don't care about the dependency.

Unfortunately, it's much more complicated to make a headless text input widget than it is to make a headless checkbox or slider. The display of text requires a tight integration between the editing code and the display code. Selection rectangles, in a bidi world, aren't just simple rectangular regions, but may contain multiple discontiguous rectangles. Splitting the text input widget into a "core" part and a "styled" part would require a complex API surface between them.

Text Input on Consoles and Mobile Devices

For Xbox, Playstation, iOS and Android platforms, we generally don't want to have a text input widget at all - instead, we want to use the platform's native text input dialog. On these platforms, the Bevy "text widget" would be swapped out for a static text field which, when activated, would bring up the native input dialog.

I don't think that this "swapping out" behavior should be integrated into the core text input widget directly. Instead, I think that there should be some higher-level, opinionated widget, controlled by feature flags, which invokes the core widget, or not, depending on the current platform. This gives the user the option to use the core widget if they really want to, even on platforms where it is not the recommended practice.

One open challenge here is how to activate the platform's text input dialog at all - I don't know if there's existing Rust code capable of doing this. We would need some kind of support library for console- and mobile-specific APIs. A secondary challenge is how to abstract this functionality into a common API so that users don't have to write a bunch of platform-specific code for editing text.

@alice-i-cecile

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-UIGraphical user interfaces, styles, layouts, and widgetsC-BugAn unexpected or incorrect behaviorC-UsabilityA targeted quality-of-life change that makes Bevy easier to useS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!

    Type

    No type

    Projects

    Status

    Widget-ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions