-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
bevy_text::input
module
#20336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
bevy_text::input
module
#20336
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments, really just nits. Fantastic work, overall!
@@ -0,0 +1,1340 @@ | |||
use crate::buffer_dimensions; | |||
use crate::load_font_to_fontdb; | |||
use crate::CosmicFontSystem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason why these don't use the more compact (multiple imports per line) format? I suppose single-line-per-import makes merging easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes just been making so many changes, probably consolidate them once it's ready to merge.
/// Any actions that modify a text input's text so that it fails | ||
/// to pass the filter are not applied. | ||
#[derive(Component)] | ||
pub enum TextInputFilter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really good. I like all the options here.
For something like a MMORPG I would probably make a custom filter for character names: alphanumeric, can have internal spaces but can't begin or end with a space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for something like name entry, it's more natural to allow arbitrary edits and then perform any validation and trim any trailing or leading spaces etc after submission.
Perhaps we add an option to only apply the filter on submit, then refuse to submit if the filter fails and emit an InvalidSubmission
event variant. It might be better left for the user to handle though, I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for something like name entry, it's more natural to allow arbitrary edits and then perform any validation and trim any trailing or leading spaces etc after submission.
You're right, that's better.
Perhaps we add an option to only apply the filter on submit, then refuse to submit if the filter fails and emit an
InvalidSubmission
event variant. It might be better left for the user to handle though, I'm not sure.
I'm going to put on my lecturer's hat and talk briefly about form validation, apologies for my long-windedness.
In the world of React.js, the common practice (which is supported by libraries such as react-hook-form
) is to have two validation modes. This validation is done at the form level, not at the level of individual fields. That is, specialized field types such as numeric inputs still only accept digits (this is built in to the browser), but for more complex formats the validation is done by the form handler.
Initially the form is set to "validate on submit" mode, meaning that validation is not done until the user attempts to submit the form. The reason is that we don't want to distract the user with error messages while they are typing. This is especially true for "required field" errors. If the validation succeeds and the form is submitted successfully, then the user never sees an error message.
If validation fails, then the form will display error messages below each field that failed validation. In addition, the form now changes to "validate on type" mode, meaning that validation is done continuously as the user is correcting their mistakes, giving them instant feedback. Once all of the errors have been corrected, they can attempt to submit again.
Libraries like react-hook-form
allow custom validators to be defined for individual fields, where the validator doesn't just return a boolean but an error message or error code, allowing the message displayed to the user to include a description of what they did wrong. In Rust, this would probably mean returning a Result
.
All this being said, I don't think we should include anything like this sort of functionality in our text input. First, because most of our users don't need to fill in complex forms, and displaying error messages below input fields is way out of scope here. If someone wants to build a third-party crate for form handling, it should be simple enough given the various events that you've defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically also you would just apply a debounce to validation so you get errors pre-submit but you don't spam your backend while you are typing. This is the pattern I've most commonly see.
crates/bevy_text/src/input.rs
Outdated
editor.set_redraw(true); | ||
} | ||
|
||
/// Apply the queued actions for each text input, with special case for submit actions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a public function, I'd like a bit more explanation on when/where/how this would be used. Is this something meant to be called by app developers, or only from widget authors?
More generally: there are a lot of public functions in this module, and I'm unclear as to the scope of their intended use: are they meant to be called only by other bevy crates, or by game developers generally? I wish there was some way to indicate this in rustdoc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure what would end up in the text module and what would go in the widget implementation so I was keeping things pub mostly while I was moving them back and forth.
Most of the systems aren't intended to be used outside of the bevy-text crate but they might need to be public or at least part of a public system set so that the schedule can be set up correctly to work with different layout algorithms.
Release note suggestion: A long-standing feature request from our users is support for text input. Whether the user is creating a new character, logging in with a username and password, or creating a new save file, it's vitally important for them to be able to enter a string of text. Unfortunately, writing a robust and feature-rich text input widget is not easy, especially one that supports all of the expected capabilities (such as undo, range selection, and scrolling). This effort is made much easier now that Bevy has incorporated the (add bullet list of features here) What we are releasing in this milestone is only the lowest-level, foundational elements of text editing. These are not complete, self-contained widgets, which will come in the next milestone, but more like a toolkit with "some assembly required". For now, you can write your own text input widgets, following the provided examples as a guide. |
* `TextInputAction` -> `TextEdit` * `TextInputActions` -> `TextEdits`
…he cursor. If there is a selection, overwrites it instead.
…d automatically with the `TextInputValue `text
Added cursor blink timer to `TextInputBuffer`
Can we split this apart to avoid blocking on the undo question? |
Add the the error and the invalid `TextEdit` to `InvalidEdit` events.
Co-authored-by: Tim Blackbird <[email protected]>
Please, please, please split it! From a user perspective it would be super dope if we had finally support for text input in bevy itself. Yes, I know that it's not 100% after this PR, but perfect is the enemy of good and 80% is more than 0%! |
Objective
Add an
input
module tobevy_text
with the components, systems and helper functions necessary for a user to implement a basic text input.Just the changes to
bevy_text
split out from #20326.Solution
Features:
TextEdit
s to make changes to the text input's buffer.TextInputValue
component that contains a copy of the buffer's text and is automatically synchronised on edits. On insertion theTextInputValue
s contents replace the current text in theTextInputBuffer
.Testing
Basic example using
bevy_sprite
for rendering: