input, change, submit on text/number/range/color/date/file/checkbox/radio/select/form/textarea and form API

This commit is contained in:
Face
2025-08-07 14:05:41 +03:00
parent c3e72093ea
commit d4aa741452
24 changed files with 745 additions and 67 deletions

View File

@@ -361,7 +361,7 @@ static func handle_visual_replacement(old_child_element_id: String, new_child_el
if old_position >= 0:
var parent_dom_node: Node = null
if parent_element_id == "body":
var main_scene = lua_api.get_node("/root/Main")
var main_scene = lua_api.get_main_scene()
if main_scene:
parent_dom_node = main_scene.website_container
else:

View File

@@ -1,8 +1,15 @@
class_name LuaEventUtils
extends RefCounted
static func is_date_button(node: Node) -> bool:
if node is DateButton:
return true
return node.has_method("init_with_date") and node.has_method("parse_date_string")
static func connect_element_event(signal_node: Node, event_name: String, subscription) -> bool:
if not signal_node:
print("ERROR: Signal node is null for event: ", event_name)
return false
match event_name:
@@ -70,6 +77,95 @@ static func connect_element_event(signal_node: Node, event_name: String, subscri
subscription.connected_signal = "focus_exited"
subscription.connected_node = signal_node
return true
"change":
# Check for DateButton first before generic button signals
if is_date_button(signal_node):
if signal_node.has_signal("date_selected"):
signal_node.date_selected.connect(subscription.lua_api._on_date_selected_text.bind(subscription))
subscription.connected_signal = "date_selected"
subscription.connected_node = signal_node
return true
else:
# Try to initialize if it has the init method
if signal_node.has_method("init"):
signal_node.init()
if signal_node.has_signal("date_selected"):
signal_node.date_selected.connect(subscription.lua_api._on_date_selected_text.bind(subscription))
subscription.connected_signal = "date_selected"
subscription.connected_node = signal_node
return true
return false
elif signal_node.has_signal("item_selected"):
signal_node.item_selected.connect(subscription.lua_api._on_input_item_selected.bind(subscription))
subscription.connected_signal = "item_selected"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("focus_exited") and (signal_node is LineEdit or signal_node is TextEdit):
# For text inputs and textareas, change event fires only on focus lost
signal_node.focus_exited.connect(subscription.lua_api._on_input_focus_lost.bind(subscription))
subscription.connected_signal = "focus_exited_change"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("value_changed"):
signal_node.value_changed.connect(subscription.lua_api._on_input_value_changed.bind(subscription))
subscription.connected_signal = "value_changed"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("color_changed"):
signal_node.color_changed.connect(subscription.lua_api._on_input_color_changed.bind(subscription))
subscription.connected_signal = "color_changed"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("toggled"):
signal_node.toggled.connect(subscription.lua_api._on_input_toggled.bind(subscription))
subscription.connected_signal = "toggled"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("file_selected"):
signal_node.file_selected.connect(subscription.lua_api._on_file_selected.bind(subscription))
subscription.connected_signal = "file_selected"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("pressed"):
signal_node.pressed.connect(subscription.lua_api._on_event_triggered.bind(subscription))
subscription.connected_signal = "pressed"
subscription.connected_node = signal_node
return true
else:
return false
"input":
# Input event fires on every keystroke for text inputs and textareas
if signal_node.has_signal("text_changed"):
# Handle different signal signatures: LineEdit passes text, TextEdit doesn't
var callback: Callable
if signal_node is LineEdit:
# LineEdit passes the new text as argument
callback = func(text: String): subscription.lua_api._on_input_text_changed(text, subscription)
else:
# TextEdit doesn't pass arguments, get text manually
callback = func(): subscription.lua_api._on_input_text_changed(signal_node.text, subscription)
signal_node.text_changed.connect(callback)
subscription.connected_signal = "text_changed"
subscription.connected_node = signal_node
subscription.callback_func = callback # Store for later disconnect
return true
else:
print("ERROR: No text_changed signal found for input event on ", signal_node.get_class())
return false
"submit":
# For form elements - look for a submit button or form container
if signal_node.has_signal("pressed"):
signal_node.pressed.connect(subscription.lua_api._on_form_submit.bind(subscription))
subscription.connected_signal = "form_submit"
subscription.connected_node = signal_node
return true
elif signal_node.has_signal("text_submitted"):
# LineEdit Enter key support
signal_node.text_submitted.connect(subscription.lua_api._on_text_submit.bind(subscription))
subscription.connected_signal = "text_submitted"
subscription.connected_node = signal_node
return true
return false
@@ -151,6 +247,41 @@ static func disconnect_subscription(subscription, lua_api) -> void:
"focus_exited":
if target_node.has_signal("focus_exited"):
target_node.focus_exited.disconnect(lua_api._on_event_triggered.bind(subscription))
"text_changed":
if target_node.has_signal("text_changed"):
# Use the stored callback function for proper disconnect
if subscription.callback_func:
target_node.text_changed.disconnect(subscription.callback_func)
else:
# Fallback for old connections
target_node.text_changed.disconnect(lua_api._on_input_text_changed.bind(subscription))
"focus_exited_change":
if target_node.has_signal("focus_exited"):
target_node.focus_exited.disconnect(lua_api._on_input_focus_lost.bind(subscription))
"value_changed":
if target_node.has_signal("value_changed"):
target_node.value_changed.disconnect(lua_api._on_input_value_changed.bind(subscription))
"color_changed":
if target_node.has_signal("color_changed"):
target_node.color_changed.disconnect(lua_api._on_input_color_changed.bind(subscription))
"toggled":
if target_node.has_signal("toggled"):
target_node.toggled.disconnect(lua_api._on_input_toggled.bind(subscription))
"item_selected":
if target_node.has_signal("item_selected"):
target_node.item_selected.disconnect(lua_api._on_input_item_selected.bind(subscription))
"file_selected":
if target_node.has_signal("file_selected"):
target_node.file_selected.disconnect(lua_api._on_file_selected.bind(subscription))
"date_selected":
if target_node.has_signal("date_selected"):
target_node.date_selected.disconnect(lua_api._on_date_selected_text.bind(subscription))
"form_submit":
if target_node.has_signal("pressed"):
target_node.pressed.disconnect(lua_api._on_form_submit.bind(subscription))
"text_submitted":
if target_node.has_signal("text_submitted"):
target_node.text_submitted.disconnect(lua_api._on_text_submit.bind(subscription))
"input":
# Only disable input processing if no other input subscriptions remain
if _count_active_input_subscriptions(lua_api) <= 1:

View File

@@ -46,7 +46,7 @@ class LuaSignal:
vm.lua_pop(1) # Pop callbacks table
connections.clear()
func fire_signal(args: Array, signal_table_ref: int = -1) -> void:
func fire_signal(args: Array) -> void:
for connection in connections:
var vm = connection.vm as LuauVM
# Get the callback function from our custom storage
@@ -173,7 +173,7 @@ static func signal_fire_handler(vm: LuauVM) -> int:
args.append(vm.lua_tovariant(i))
# Fire the signal with the signal table reference
lua_signal.fire_signal(args, lua_signal.signal_table_ref)
lua_signal.fire_signal(args)
return 0

View File

@@ -64,7 +64,7 @@ static func time_date_handler(vm: LuauVM) -> int:
static func time_sleep_handler(vm: LuauVM) -> int:
vm.luaL_checknumber(1)
var seconds = vm.lua_tonumber(1)
var milliseconds = int(seconds * 1000)
var _milliseconds = int(seconds * 1000)
# TODO: implement a proper sleep function
@@ -81,10 +81,10 @@ static func time_benchmark_handler(vm: LuauVM) -> int:
var error_msg = vm.lua_tostring(-1)
vm.lua_pop(1)
var end_time = Time.get_ticks_msec()
var elapsed_ms = end_time - start_time
var end = Time.get_ticks_msec()
var elapsed = end - start_time
vm.lua_pushnumber(elapsed_ms / 1000.0)
vm.lua_pushnumber(elapsed / 1000.0)
vm.lua_pushstring("Error: " + error_msg)
return 2

View File

@@ -8,10 +8,8 @@ static func match_element(selector: String, element: HTMLParser.HTMLElement) ->
var rule = CSSParser.CSSRule.new()
rule.init(selector)
var class_names = HTMLParser.extract_class_names(element)
var stylesheet = CSSParser.CSSStylesheet.new()
return stylesheet.selector_matches(rule, element.tag_name, "", class_names, element)
return stylesheet.selector_matches(rule, "", element)
static func find_all_matching(selector: String, elements: Array[HTMLParser.HTMLElement]) -> Array[HTMLParser.HTMLElement]:
var matches: Array[HTMLParser.HTMLElement] = []