input (radio/password), max/min length, pattern

This commit is contained in:
Face
2025-07-23 18:09:16 +03:00
parent a9ce6cb178
commit a899f3637e
15 changed files with 376 additions and 105 deletions

View File

@@ -1,23 +1,93 @@
extends Control
static var button_groups: Dictionary = {}
func init(element: HTMLParser.HTMLElement) -> void:
var line_edit: LineEdit = $LineEdit
var check_box: CheckBox = $CheckBox
var radio_button: CheckBox = $RadioButton
var input_type = element.get_attribute("type").to_lower()
var placeholder = element.get_attribute("placeholder")
var value = element.get_attribute("value")
var group = element.get_attribute("group")
var minlength = element.get_attribute("minlength")
var maxlength = element.get_attribute("maxlength")
var pattern = element.get_attribute("pattern")
# Hide all inputs initially
line_edit.visible = false
check_box.visible = false
radio_button.visible = false
match input_type:
"checkbox":
line_edit.visible = false
check_box.visible = true
if value and value == "true": check_box.button_pressed = true
custom_minimum_size = check_box.size
"radio":
radio_button.visible = true
radio_button.toggle_mode = true
if value and value == "true": radio_button.button_pressed = true
custom_minimum_size = radio_button.size
if group.length() > 0:
if not button_groups.has(group):
button_groups[group] = ButtonGroup.new()
radio_button.button_group = button_groups[group]
"password":
line_edit.visible = true
line_edit.secret = true
custom_minimum_size = line_edit.size
setup_text_input(line_edit, placeholder, value, minlength, maxlength, pattern)
_: # Default to text input
line_edit.visible = true
check_box.visible = false
line_edit.secret = false
custom_minimum_size = line_edit.size
if placeholder: line_edit.placeholder_text = placeholder
if value: line_edit.text = value
setup_text_input(line_edit, placeholder, value, minlength, maxlength, pattern)
func setup_text_input(line_edit: LineEdit, placeholder: String, value: String, minlength: String, maxlength: String, pattern: String) -> void:
if placeholder: line_edit.placeholder_text = placeholder
if value: line_edit.text = value
line_edit.max_length = maxlength.to_int()
if minlength.length() > 0 or pattern.length() > 0:
line_edit.text_changed.connect(_on_text_changed.bind(minlength, pattern))
func _on_text_changed(new_text: String, minlength: String, pattern: String) -> void:
var line_edit = get_node("LineEdit") as LineEdit
var is_valid = true
# Check minimum length
if minlength.length() > 0 and minlength.is_valid_int():
var min_len = minlength.to_int()
if new_text.length() < min_len and new_text.length() > 0:
is_valid = false
# Check pattern (regex)
if pattern.length() > 0 and new_text.length() > 0:
var regex = RegEx.new()
if regex.compile(pattern) == OK:
if not regex.search(new_text):
is_valid = false
if is_valid:
# Reset to default styles
line_edit.remove_theme_stylebox_override("normal")
line_edit.remove_theme_stylebox_override("focus")
line_edit.modulate = Color.WHITE
else:
var normal_style = create_red_border_style_from_theme(line_edit, "normal")
var focus_style = create_red_border_style_from_theme(line_edit, "focus")
line_edit.add_theme_stylebox_override("normal", normal_style)
line_edit.add_theme_stylebox_override("focus", focus_style)
line_edit.modulate = Color.WHITE
func create_red_border_style_from_theme(line_edit: LineEdit, style_name: String) -> StyleBoxFlat:
var original_style: StyleBoxFlat = line_edit.get_theme_stylebox(style_name)
var style: StyleBoxFlat = original_style.duplicate()
style.border_color = Color.RED
return style

View File

@@ -74,15 +74,28 @@ both spaces and
line breaks
</pre>
<form>
<!-- action, method, and type=submit are for when we implement Lua -->
<form action=\"/submit\" method=\"POST\">
<span>Name:</span>
<input type=\"text\" placeholder=\"First name\" value=\"John\" />
<span>Surname:</span>
<input type=\"text\" placeholder=\"Last name\" value=\"Doe\" />
<input type=\"text\" placeholder=\"First name\" value=\"John\" maxlength=\"20\" minlength=\"3\" />
<span>Email regex:</span>
<input type=\"text\" placeholder=\"Last name\" value=\"Doe\" pattern=\"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$\" />
<span>Smart:</span>
<input type=\"checkbox\" />
<input type=\"checkbox\" value=\"true\" />
<button>Submit</button>
<p>favorite food</p>
<input type=\"radio\" group=\"food\" />
<span>Pizza</span>
<input type=\"radio\" group=\"food\" />
<span>Berry</span>
<input type=\"radio\" group=\"food\" />
<span>Gary</span>
<input type=\"password\" placeholder=\"your password...\" />
<button type=\"submit\">Submit</button>
</form>
<separator direction=\"horizontal\" />
@@ -138,89 +151,14 @@ line breaks
continue
match element.tag_name:
"p":
var p = P.instantiate()
p.init(element)
website_container.add_child(p)
if contains_hyperlink(element):
p.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"h1":
var h1 = H1.instantiate()
h1.init(element)
website_container.add_child(h1)
if contains_hyperlink(element):
h1.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"h2":
var h2 = H2.instantiate()
h2.init(element)
website_container.add_child(h2)
if contains_hyperlink(element):
h2.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"h3":
var h3 = H3.instantiate()
h3.init(element)
website_container.add_child(h3)
if contains_hyperlink(element):
h3.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"h4":
var h4 = H4.instantiate()
h4.init(element)
website_container.add_child(h4)
if contains_hyperlink(element):
h4.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"h5":
var h5 = H5.instantiate()
h5.init(element)
website_container.add_child(h5)
if contains_hyperlink(element):
h5.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"h6":
var h6 = H6.instantiate()
h6.init(element)
website_container.add_child(h6)
if contains_hyperlink(element):
h6.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
"pre":
var pre = PRE.instantiate()
pre.init(element)
website_container.add_child(pre)
"br":
var br = BR.instantiate()
br.init(element)
website_container.add_child(br)
"img":
var img = IMG.instantiate()
img.init(element)
website_container.add_child(img)
"separator":
var separator = SEPARATOR.instantiate()
separator.init(element)
website_container.add_child(separator)
"form":
var form = FORM.instantiate()
form.init(element)
website_container.add_child(form)
# Render form children
for child_element in element.children:
var child_node = create_element_node(child_element)
if child_node:
form.add_child(child_node)
"input":
var input = INPUT.instantiate()
input.init(element)
website_container.add_child(input)
"button":
var button = BUTTON.instantiate()
button.init(element)
website_container.add_child(button)
"span":
var span = SPAN.instantiate()
span.init(element)
website_container.add_child(span)
_:
print("Couldn't parse unsupported HTML tag \"%s\"" % element.tag_name)
var element_node = create_element_node(element)
if element_node:
website_container.add_child(element_node)
# Handle hyperlinks for all elements
if contains_hyperlink(element) and element_node.has_method("get") and element_node.get("rich_text_label"):
element_node.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
else:
print("Couldn't parse unsupported HTML tag \"%s\"" % element.tag_name)
i += 1
@@ -254,18 +192,60 @@ func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
return false
func create_element_node(element: HTMLParser.HTMLElement) -> Control:
var node: Control = null
match element.tag_name:
"p":
node = P.instantiate()
node.init(element)
"h1":
node = H1.instantiate()
node.init(element)
"h2":
node = H2.instantiate()
node.init(element)
"h3":
node = H3.instantiate()
node.init(element)
"h4":
node = H4.instantiate()
node.init(element)
"h5":
node = H5.instantiate()
node.init(element)
"h6":
node = H6.instantiate()
node.init(element)
"pre":
node = PRE.instantiate()
node.init(element)
"br":
node = BR.instantiate()
node.init(element)
"img":
node = IMG.instantiate()
node.init(element)
"separator":
node = SEPARATOR.instantiate()
node.init(element)
"form":
node = FORM.instantiate()
node.init(element)
for child_element in element.children:
var child_node = create_element_node(child_element)
if child_node:
node.add_child(child_node)
"input":
var input = INPUT.instantiate()
input.init(element)
return input
node = INPUT.instantiate()
node.init(element)
"button":
var button = BUTTON.instantiate()
button.init(element)
return button
node = BUTTON.instantiate()
node.init(element)
"span":
var span = SPAN.instantiate()
span.init(element)
return span
node = SPAN.instantiate()
node.init(element)
_:
return null
return node