Godot Version
4.2.2
Question
Hi, all,
I am using a LineEdit node in a form. It uses the text_changed signal to validate input. I am restricting input to numbers, decimal point, and minus sign. The virtual keyboard is enabled for mobile input, and the keyboard pops up fine when testing on device.
The input works successfully when I use the regular keyboard. Numbers, decimal, and negative all work, and other characters are not returned to the LineEdit.
When I change the Virtual Keyboard to Decimal or Number from Default, the decimal and minus sign don’t work. Strangely, they do work if I select the Phone keyboard. I am currently testing on Android, haven’t tried iOS yet.
I have tried two different functions to check for valid characters:
func _is_valid_number(text):
return text == "-" or text == "." or text.match("^[-]?[0-9]*[.]?[0-9]*$")
and
func is_digit(character: String) -> bool:
return "-0123456789.".find(character) != -1
I am thinking that the Number and Decimal keyboards are sending a different character code for “-” and “.” than the regular keyboard. Any thoughts on this?
Yes the keypad keys are a different scan code. But that isn’t in play here.
Neither of your functions are actually going to work. I don’t think you completely tested them.
The first function _is_valid_number() doesn’t match the keyboard numbers. It does find the “-” and the “.” but 0 -9 from either keyboard or keypad doesn’t match.
The second function breaks down if character length > 1. How would you match -1?
If you send one character at a time it will return true on 1-
Is there any reason to not use the String classes built in is_valid_float() function?
@onready var line_edit: LineEdit = $LineEdit
var old_text:String = ""
func _on_line_edit_text_changed(new_text: String) -> void:
if new_text.is_valid_float():
old_text = new_text
else:
line_edit.text = old_text
If the ‘e’ is not welcome then try this guys regex.
I should have been more clear about the functions. They are each checking single characters for the string in the LineEdit. Either by finding the character at the caret position or iterating through the entire LineEdit text field. Thus, it would never be matching “-1”. It would match “-” and “1”.
But, more importantly, it turns out that the issue with the number keyboard is separate from this. See Android release does not allow for decimal values on number or decimal type virt keyboard when using line edit with RegEx · Issue #84091 · godotengine/godot · GitHub The decimal won’t type in the field with or without validation.
I am going to make my own keyboard at this point. I can then style it and customize it to match my game better, anyway.
Both of them work and test correctly.
The first function does not work on my system. In fact I believe this may be a GDScript/GODOT bug as I could get no pattern to match whatsoever.
It isn’t a keyboard issue since you aren’t testing scan codes (and it converts to ascii which should work in the example I show) .
You are testing Strings and they are only ever strings no matter what the vehicle that gets them to your function.
Here we bypass keyboard and just send Strings to the function in question.
func _is_valid_number(text):
return text == "-" or text == "." or text.match("^[-]?[0-9]*[.]?[0-9]*$")
func _ready() -> void:
printt(_is_valid_number("-"), _is_valid_number("3"))
The output from this is:
true false
It found the hyphen but it did not find the 3.
Since the parameter text is a variant I also tried sending it the number 3 which of course also failed.
If I change the test to specifically look for 3 it does find a match:
func _is_valid_number(text):
return text == "-" or text == "." or text.match("3")
I tried to find a bug report but I found nothing.
Is it my system? Maybe. Windows 7.
I apologize for the confusion.
That is strange. Both methods are working for me. It might help to see the entire code, including the function call from the signal.
(You can see “pass” in the middle of _on_blank_text_changed. I was using that to skip the remaining code instead of commenting it out for testing.) I prefer the code below pass since it avoids calling ‘self.’ I find references to self to get wonky when a scene is in another scene. This entire LineEdit is saved as a scene that I used elsewhere repeatedly.
While I hope this is making sense and might (or not) be helpful for anyone, it is a path I am abandoning. The whole thing is moot, since I can’t get negatives or decimals into a LineEdit from the Number virtual keyboard even without filtering. It only works with the default or phone virtual keyboards.
func _on_blank_text_changed(new_text: String, sender: LineEdit):
# Filter input using is_valid_number
if not _is_valid_number(new_text):
# If the input is invalid, revert to the previous valid text
self.text = self.text.substr(0, self.caret_position - 1) + self.text.substr(self.caret_position)
pass # rest of code below is another way to test for valid text using is_digit for each char in string
var filtered_text = ""
for character in new_text:
if is_digit(character):
filtered_text += character
sender.text = filtered_text
sender.caret_column = filtered_text.length()
func _is_valid_number(text):
return text == "-" or text == "." or text == "-." or text.match("^[-]?[0-9]*[.]?[0-9]*$")
func is_digit(character: String) -> bool:
return "-0123456789.".find(character) != -1
func _on_text_changed(new_text):
# Filter input to allow only numerical values
var filtered_text = ""
for character in new_text:
if is_digit(character):
filtered_text += character
self.text = filtered_text
self.caret_column = filtered_text.length()
The first signal needs to be created in code, since there is no way to add a parameter that references the LineEdit object when connecting the signal from the Node panel.
@onready var blank = $"."
.
.
.
blank.connect("text_changed", Callable(self, "_on_blank_text_changed").bind(blank))
on_text_changed is from directly connecting the signal in the Node panel
I just tried a very simple pattern match on the latest Ubuntu OS 24.x and it can’t resolve simple patterns.
func _ready() -> void:
printt("3".match("3"), "3".match("[1234]"), "3".match("[3]"), "3".match("*"), "3".match("[123]*"))
printt("a".match("a"), "a".match("[abcde]"), "a".match("[a]"))
output:
true false false true false
true false false
I tried the same regex on Python and it had no problem finding matches.
I am reasonably sure this is a bug.
So I posted a bug report.
pass
does not cause any subsequent code to skip. It literally does nothing, and has no use except to create an empty code block (e.g. an empty function body).
Both of them work and test correctly.
In my last post I linked a bug report. The reply to that was pretty straight forward.
In GODOT the Match function only supports * and ? and does NOT support list and range ‘[’ ‘]’.
Ergo, there was never a time when your match pattern worked. I am disappointed in you doubling down on that point and myself for buying into it.
This statement:
text.match("^[-]?[0-9]*[.]?[0-9]*$")
…did not ever match anything.
Quote from the bug report:
It doesn’t support this syntax,
OP, this was not a very nice way to treat someone who was trying to help you.
(I only post this now to clear up confusion any other visitor to this thread may have.)
My apologies, if you took offense. None was intended, and I wan’t trying to discount your input, just trying to clarify what I was attempting.
I thought my code worked, but I was making a call to the other function, which did work. Trying to set up signalling two different ways muddled what I intended to do. I certainly appreciate your follow through, and it is a good lesson about my confusion when gdscript and python are very similar yet also very different.
The only workaround I could find for using the Number Virtual keyboard ( including “.” and “-”) was to make a custom one that sends text to whatever LineEdit has focus.