input, change, submit on text/number/range/color/date/file/checkbox/radio/select/form/textarea and form API
This commit is contained in:
@@ -34,7 +34,7 @@ class HTMLElement:
|
||||
func get_preserved_text() -> String:
|
||||
return text_content
|
||||
|
||||
func get_bbcode_formatted_text(parser: HTMLParser = null) -> String:
|
||||
func get_bbcode_formatted_text(parser: HTMLParser) -> String:
|
||||
var styles = {}
|
||||
if parser != null:
|
||||
styles = parser.get_element_styles_with_inheritance(self, "", [])
|
||||
@@ -142,8 +142,7 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = "
|
||||
|
||||
var styles = {}
|
||||
|
||||
var class_names = extract_class_names(element)
|
||||
styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element))
|
||||
styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(event, element))
|
||||
# Apply inline styles (higher priority) - force override CSS rules
|
||||
var inline_style = element.get_attribute("style")
|
||||
if inline_style.length() > 0:
|
||||
@@ -169,8 +168,7 @@ func get_element_styles_internal(element: HTMLElement, event: String = "") -> Di
|
||||
|
||||
# Apply CSS rules
|
||||
if parse_result.css_parser:
|
||||
var class_names = extract_class_names(element)
|
||||
styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element))
|
||||
styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(event, element))
|
||||
|
||||
# Apply inline styles (higher priority) - force override CSS rules
|
||||
var inline_style = element.get_attribute("style")
|
||||
@@ -203,7 +201,7 @@ func parse_inline_style_with_event(style_string: String, event: String = "") ->
|
||||
else:
|
||||
# Check if this is a CSS class that might have pseudo-class rules
|
||||
if parse_result.css_parser and parse_result.css_parser.stylesheet:
|
||||
var pseudo_styles = parse_result.css_parser.stylesheet.get_styles_for_element("", event, [utility_name], null)
|
||||
var pseudo_styles = parse_result.css_parser.stylesheet.get_styles_for_element(event, null)
|
||||
if not pseudo_styles.is_empty():
|
||||
for property in pseudo_styles:
|
||||
properties[property] = pseudo_styles[property]
|
||||
@@ -287,7 +285,7 @@ func find_by_id(element_id: String) -> HTMLElement:
|
||||
|
||||
return null
|
||||
|
||||
func register_dom_node(element: HTMLElement, node: Control) -> void:
|
||||
func register_dom_node(element: HTMLElement, node) -> void:
|
||||
var element_id = element.get_id()
|
||||
if element_id.length() > 0:
|
||||
parse_result.dom_nodes[element_id] = node
|
||||
|
||||
@@ -10,6 +10,7 @@ class EventSubscription:
|
||||
var lua_api: LuaAPI
|
||||
var connected_signal: String = ""
|
||||
var connected_node: Node = null
|
||||
var callback_func: Callable
|
||||
|
||||
var dom_parser: HTMLParser
|
||||
var event_subscriptions: Dictionary = {}
|
||||
@@ -489,6 +490,8 @@ func _element_on_event_handler(vm: LuauVM) -> int:
|
||||
|
||||
var signal_node = get_dom_node(dom_node, "signal")
|
||||
var success = LuaEventUtils.connect_element_event(signal_node, event_name, subscription)
|
||||
if not success:
|
||||
print("ERROR: Failed to connect ", event_name, " event for ", element_id)
|
||||
|
||||
return _handle_subscription_result(vm, subscription, success)
|
||||
|
||||
@@ -641,6 +644,11 @@ func _execute_lua_callback(subscription: EventSubscription, args: Array = []) ->
|
||||
else:
|
||||
subscription.vm.lua_pop(1)
|
||||
|
||||
func _execute_input_event_callback(subscription: EventSubscription, event_data: Dictionary) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
_execute_lua_callback(subscription, [event_data])
|
||||
|
||||
# Global input processing
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey:
|
||||
@@ -699,6 +707,108 @@ func _handle_mousemove_event(mouse_event: InputEventMouseMotion, subscription: E
|
||||
}
|
||||
_execute_lua_callback(subscription, [mouse_info])
|
||||
|
||||
# Input event handlers
|
||||
func _on_input_text_changed(new_text: String, subscription: EventSubscription) -> void:
|
||||
_execute_input_event_callback(subscription, {"value": new_text})
|
||||
|
||||
func _on_input_focus_lost(subscription: EventSubscription) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
|
||||
# Get the current text value from the input node
|
||||
var dom_node = dom_parser.parse_result.dom_nodes.get(subscription.element_id, null)
|
||||
if dom_node:
|
||||
var current_text = ""
|
||||
if dom_node.has_method("get_text"):
|
||||
current_text = dom_node.get_text()
|
||||
elif "text" in dom_node:
|
||||
current_text = dom_node.text
|
||||
|
||||
var event_info = {"value": current_text}
|
||||
_execute_lua_callback(subscription, [event_info])
|
||||
|
||||
func _on_input_value_changed(new_value, subscription: EventSubscription) -> void:
|
||||
_execute_input_event_callback(subscription, {"value": new_value})
|
||||
|
||||
func _on_input_color_changed(new_color: Color, subscription: EventSubscription) -> void:
|
||||
_execute_input_event_callback(subscription, {"value": "#" + new_color.to_html(false)})
|
||||
|
||||
func _on_input_toggled(pressed: bool, subscription: EventSubscription) -> void:
|
||||
_execute_input_event_callback(subscription, {"value": pressed})
|
||||
|
||||
func _on_input_item_selected(index: int, subscription: EventSubscription) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
|
||||
# Get value from OptionButton
|
||||
var dom_node = dom_parser.parse_result.dom_nodes.get(subscription.element_id, null)
|
||||
var value = ""
|
||||
var text = ""
|
||||
|
||||
if dom_node and dom_node is OptionButton:
|
||||
var option_button = dom_node as OptionButton
|
||||
text = option_button.get_item_text(index)
|
||||
# Get actual value attribute (stored as metadata)
|
||||
var metadata = option_button.get_item_metadata(index)
|
||||
value = str(metadata) if metadata != null else text
|
||||
|
||||
var event_info = {"index": index, "value": value, "text": text}
|
||||
_execute_lua_callback(subscription, [event_info])
|
||||
|
||||
func _on_file_selected(file_path: String, subscription: EventSubscription) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
|
||||
var dom_node = dom_parser.parse_result.dom_nodes.get(subscription.element_id, null)
|
||||
|
||||
if dom_node:
|
||||
var file_container = dom_node.get_parent() # FileContainer (HBoxContainer)
|
||||
if file_container:
|
||||
var input_element = file_container.get_parent() # Input Control
|
||||
if input_element and input_element.has_method("get_file_info"):
|
||||
var file_info = input_element.get_file_info()
|
||||
if not file_info.is_empty():
|
||||
_execute_lua_callback(subscription, [file_info])
|
||||
return
|
||||
|
||||
# Fallback
|
||||
var file_name = file_path.get_file()
|
||||
_execute_lua_callback(subscription, [{"fileName": file_name}])
|
||||
|
||||
func _on_date_selected_text(date_text: String, subscription: EventSubscription) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
|
||||
var event_info = {"value": date_text}
|
||||
_execute_lua_callback(subscription, [event_info])
|
||||
|
||||
func _on_form_submit(subscription: EventSubscription) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
|
||||
# Find parent form
|
||||
var form_data = {}
|
||||
var element = dom_parser.find_by_id(subscription.element_id)
|
||||
if element:
|
||||
var form_element = element.parent
|
||||
while form_element and form_element.tag_name != "form":
|
||||
form_element = form_element.parent
|
||||
|
||||
if form_element:
|
||||
var form_dom_node = dom_parser.parse_result.dom_nodes.get(form_element.get_attribute("id"), null)
|
||||
if form_dom_node and form_dom_node.has_method("submit_form"):
|
||||
form_data = form_dom_node.submit_form()
|
||||
|
||||
var event_info = {"data": form_data}
|
||||
_execute_lua_callback(subscription, [event_info])
|
||||
|
||||
func _on_text_submit(text: String, subscription: EventSubscription) -> void:
|
||||
if not event_subscriptions.has(subscription.id):
|
||||
return
|
||||
|
||||
var event_info = {"value": text}
|
||||
_execute_lua_callback(subscription, [event_info])
|
||||
|
||||
# DOM node utilities
|
||||
func get_dom_node(node: Node, purpose: String = "general") -> Node:
|
||||
if not node:
|
||||
@@ -717,6 +827,10 @@ func get_dom_node(node: Node, purpose: String = "general") -> Node:
|
||||
return node.get("rich_text_label")
|
||||
elif node.get_node_or_null("RichTextLabel"):
|
||||
return node.get_node_or_null("RichTextLabel")
|
||||
elif node is LineEdit or node is TextEdit or node is SpinBox or node is HSlider:
|
||||
return node
|
||||
elif node is CheckBox or node is ColorPickerButton or node is OptionButton:
|
||||
return node
|
||||
else:
|
||||
return node
|
||||
"text":
|
||||
|
||||
@@ -871,6 +871,281 @@ var HTML_CONTENT_DOM_MANIPULATION = """
|
||||
""".to_utf8_buffer()
|
||||
|
||||
var HTML_CONTENT = """<head>
|
||||
<title>Input Events API Demo</title>
|
||||
<icon src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Lua-Logo.svg/256px-Lua-Logo.svg.png">
|
||||
<meta name="theme-color" content="#059669">
|
||||
<meta name="description" content="Demonstrating input element events with GURT Lua API">
|
||||
|
||||
<style>
|
||||
body { bg-[#f0fdf4] p-6 }
|
||||
h1 { text-[#059669] text-3xl font-bold text-center }
|
||||
h2 { text-[#047857] text-xl font-semibold }
|
||||
.container { bg-[#ffffff] p-6 rounded-lg shadow-lg max-w-4xl mx-auto }
|
||||
.form-group { flex flex-col gap-2 mb-4 }
|
||||
.input-demo { bg-[#f8fafc] p-4 rounded-lg border }
|
||||
.event-log { bg-[#1f2937] text-white p-4 rounded-lg font-mono text-sm max-h-64 overflow-auto }
|
||||
.form-button { bg-[#10b981] text-white px-4 py-2 rounded hover:bg-[#059669] cursor-pointer }
|
||||
.clear-btn { bg-[#ef4444] text-white px-4 py-2 rounded hover:bg-[#dc2626] cursor-pointer }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
-- Get the event log element
|
||||
local eventLog = gurt.select('#event-log')
|
||||
|
||||
local function logEvent(elementType, eventType, data)
|
||||
local timestamp = Time.format(Time.now(), '%H:%M:%S')
|
||||
local message = '[' .. timestamp .. '] ' .. elementType .. ' -> ' .. eventType
|
||||
if data then
|
||||
message = message .. ': ' .. data
|
||||
end
|
||||
|
||||
print(message)
|
||||
end
|
||||
|
||||
-- Text Input Events
|
||||
local textInput = gurt.select('#text-input')
|
||||
print('Text input found:', textInput)
|
||||
if textInput then
|
||||
textInput:on('input', function(e)
|
||||
logEvent('Text Input', 'input', e.value)
|
||||
end)
|
||||
textInput:on('change', function(e)
|
||||
logEvent('Text Input', 'change', e.value)
|
||||
end)
|
||||
textInput:on('focusin', function()
|
||||
logEvent('Text Input', 'focusin', nil)
|
||||
end)
|
||||
textInput:on('focusout', function()
|
||||
logEvent('Text Input', 'focusout', nil)
|
||||
end)
|
||||
else
|
||||
print('Text input not found!')
|
||||
end
|
||||
|
||||
-- Email Input Events
|
||||
local emailInput = gurt.select('#email-input')
|
||||
|
||||
emailInput:on('input', function(e)
|
||||
logEvent('Email Input', 'input', e.value)
|
||||
end)
|
||||
emailInput:on('change', function(e)
|
||||
logEvent('Email Input', 'change', e.value)
|
||||
end)
|
||||
|
||||
-- Password Input Events
|
||||
local passwordInput = gurt.select('#password-input')
|
||||
passwordInput:on('input', function(e)
|
||||
logEvent('Password Input', 'input', table.tostring(e))
|
||||
end)
|
||||
passwordInput:on('change', function(e)
|
||||
logEvent('Password Input', 'change', table.tostring(e))
|
||||
end)
|
||||
|
||||
-- Number Input Events
|
||||
local numberInput = gurt.select('#number-input')
|
||||
numberInput:on('change', function(e)
|
||||
logEvent('Number Input', 'change', e.value)
|
||||
end)
|
||||
|
||||
-- Range Input Events
|
||||
local rangeInput = gurt.select('#range-input')
|
||||
rangeInput:on('change', function(e)
|
||||
logEvent('Range Input', 'change', e.value)
|
||||
end)
|
||||
|
||||
-- Color Input Events
|
||||
local colorInput = gurt.select('#color-input')
|
||||
colorInput:on('change', function(e)
|
||||
logEvent('Color Input', 'change', e.value)
|
||||
end)
|
||||
|
||||
-- Date Input Events
|
||||
local dateInput = gurt.select('#date-input')
|
||||
dateInput:on('change', function(e)
|
||||
logEvent('Date Input', 'change', e.value)
|
||||
end)
|
||||
|
||||
-- File Input Events
|
||||
local fileInput = gurt.select('#file-input')
|
||||
fileInput:on('change', function(e)
|
||||
if e.dataURL then
|
||||
e.dataURL = e.dataURL:sub(1, 100)
|
||||
end
|
||||
if e.text then
|
||||
e.text = e.text:sub(1, 100)
|
||||
end
|
||||
logEvent('File Input', 'change', table.tostring(e))
|
||||
end)
|
||||
|
||||
-- Checkbox Events
|
||||
local checkbox = gurt.select('#checkbox')
|
||||
checkbox:on('change', function(e)
|
||||
logEvent('Checkbox', 'change', table.tostring(e))
|
||||
end)
|
||||
|
||||
-- Radio Button Events
|
||||
local radio1 = gurt.select('#radio1')
|
||||
local radio2 = gurt.select('#radio2')
|
||||
local radio3 = gurt.select('#radio3')
|
||||
|
||||
radio1:on('change', function(e)
|
||||
logEvent('Radio Button 1', 'change', table.tostring(e))
|
||||
end)
|
||||
|
||||
radio2:on('change', function(e)
|
||||
logEvent('Radio Button 2', 'change', table.tostring(e))
|
||||
end)
|
||||
|
||||
radio3:on('change', function(e)
|
||||
logEvent('Radio Button 3', 'change', table.tostring(e))
|
||||
end)
|
||||
|
||||
-- Textarea Events
|
||||
local textarea = gurt.select('#textarea')
|
||||
|
||||
textarea:on('input', function(e)
|
||||
logEvent('Textarea', 'input', e.value:sub(1, 20) .. '...')
|
||||
end)
|
||||
textarea:on('change', function(e)
|
||||
logEvent('Textarea', 'change', 'Length: ' .. #e.value)
|
||||
end)
|
||||
|
||||
-- Select Events
|
||||
local selectElement = gurt.select('#select-element')
|
||||
selectElement:on('change', function(e)
|
||||
logEvent('Select', 'change', 'Index: ' .. table.tostring(e))
|
||||
end)
|
||||
|
||||
-- Button Events
|
||||
local submitBtn = gurt.select('#submit-btn')
|
||||
submitBtn:on('click', function()
|
||||
logEvent('Submit Button', 'click', nil)
|
||||
end)
|
||||
submitBtn:on('submit', function(e)
|
||||
logEvent('Form', 'submit', table.tostring(e))
|
||||
end)
|
||||
|
||||
-- Clear log button
|
||||
local clearBtn = gurt.select('#clear-btn')
|
||||
clearBtn:on('click', function()
|
||||
eventLog.text = '--- Event Log Cleared ---\\n'
|
||||
eventCount = 0
|
||||
end)
|
||||
|
||||
-- Initial log message
|
||||
logEvent('System', 'initialized', 'Input events demo ready')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>🎛️ Input Events API Demo</h1>
|
||||
|
||||
<div style="container mt-6">
|
||||
<div style="flex flex-row gap-6">
|
||||
<!-- Input Forms Column -->
|
||||
<div style="flex-1">
|
||||
<h2>Input Elements</h2>
|
||||
|
||||
<form id="demo-form">
|
||||
<div style="form-group">
|
||||
<label>Text Input:</label>
|
||||
<input id="text-input" key="username" type="text" placeholder="Type something..." />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Email Input:</label>
|
||||
<input id="email-input" key="email" type="email" placeholder="Enter email..." />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Password Input:</label>
|
||||
<input id="password-input" key="password" type="password" placeholder="Enter password..." />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Number Input:</label>
|
||||
<input id="number-input" key="score" type="number" min="0" max="100" value="50" />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Range Input:</label>
|
||||
<input id="range-input" key="volume" type="range" min="0" max="100" value="25" />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Color Input:</label>
|
||||
<input id="color-input" key="favoriteColor" type="color" value="#ff0000" />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Date Input:</label>
|
||||
<input id="date-input" key="birthDate" type="date" value="2024-01-01" />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>File Input:</label>
|
||||
<input id="file-input" key="attachment" type="file" accept=".txt,.pdf,.png" />
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<input id="checkbox" key="newsletter" type="checkbox" />
|
||||
<p>Checkbox Option</p>
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<input id="radio1" key="option" type="radio" group="options" value="option1" /><span>Option 1</span>
|
||||
<input id="radio2" key="option" type="radio" group="options" value="option2" /><span>Option 2</span>
|
||||
<input id="radio3" key="option" type="radio" group="options" value="option3" /><span>Option 3</span>
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Textarea:</label>
|
||||
<textarea id="textarea" key="message" placeholder="Write something longer..." rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div style="form-group">
|
||||
<label>Select Dropdown:</label>
|
||||
<select id="select-element" key="fruit">
|
||||
<option value="apple">Apple</option>
|
||||
<option value="banana" selected="true">Banana</option>
|
||||
<option value="orange">Orange</option>
|
||||
<option value="grape">Grape</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="flex gap-2">
|
||||
<button id="submit-btn" type="submit" style="form-button">Submit Form</button>
|
||||
<button id="clear-btn" type="button" style="clear-btn">Clear Log</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Event Log Column -->
|
||||
<div style="flex-1">
|
||||
<h2>Event Log</h2>
|
||||
<div style="event-log">
|
||||
<pre id="event-log">Waiting for events...
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div style="bg-[#e0f2fe] p-4 rounded-lg mt-4">
|
||||
<h3 style="text-[#0277bd] font-semibold mb-2">Available Events:</h3>
|
||||
<ul style="text-[#01579b] space-y-1 text-sm">
|
||||
<li><strong>input:</strong> Fires as you type (real-time)</li>
|
||||
<li><strong>change:</strong> Fires when value changes and element loses focus</li>
|
||||
<li><strong>focusin:</strong> Fires when element gains focus</li>
|
||||
<li><strong>focusout:</strong> Fires when element loses focus</li>
|
||||
<li><strong>click:</strong> Fires when button is clicked</li>
|
||||
<li><strong>submit:</strong> Fires when form is submitted (includes form data)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
""".to_utf8_buffer()
|
||||
|
||||
var HTML_CONTENT_CLIPBOARD = """<head>
|
||||
<title>Network & Clipboard API Demo</title>
|
||||
<icon src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Lua-Logo.svg/256px-Lua-Logo.svg.png">
|
||||
<meta name="theme-color" content="#059669">
|
||||
|
||||
@@ -39,7 +39,7 @@ static func load_web_font(font_info: Dictionary) -> void:
|
||||
|
||||
http_request.timeout = 30.0
|
||||
|
||||
http_request.request_completed.connect(func(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
|
||||
http_request.request_completed.connect(func(_result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray):
|
||||
if response_code == 200:
|
||||
|
||||
if body.size() > 0:
|
||||
|
||||
@@ -12,7 +12,7 @@ func _ready():
|
||||
child_entered_tree.connect(_on_child_added)
|
||||
child_exiting_tree.connect(_on_child_removed)
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
list_type = element.get_attribute("type").to_lower()
|
||||
if list_type == "":
|
||||
list_type = "disc" if not is_ordered else "decimal"
|
||||
@@ -67,7 +67,7 @@ func calculate_marker_width(element: HTMLParser.HTMLElement) -> float:
|
||||
|
||||
return max(width, 20.0 if not is_ordered else 30.0)
|
||||
|
||||
func create_li_node(element: HTMLParser.HTMLElement, index: int, parser: HTMLParser = null) -> Control:
|
||||
func create_li_node(element: HTMLParser.HTMLElement, index: int, parser: HTMLParser) -> Control:
|
||||
var li_container = HBoxContainer.new()
|
||||
|
||||
# Create marker
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
extends Control
|
||||
|
||||
func init(_element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
func init(_element: HTMLParser.HTMLElement, _parser: HTMLParser) -> void:
|
||||
pass
|
||||
|
||||
@@ -4,7 +4,7 @@ extends HBoxContainer
|
||||
var current_element: HTMLParser.HTMLElement
|
||||
var current_parser: HTMLParser
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
current_element = element
|
||||
current_parser = parser
|
||||
var button_node: Button = $ButtonNode
|
||||
@@ -26,6 +26,8 @@ func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
button_node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
apply_button_styles(element, parser)
|
||||
|
||||
parser.register_dom_node(element, button_node)
|
||||
|
||||
func apply_button_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
if not element or not parser:
|
||||
@@ -150,12 +152,6 @@ func apply_button_color_with_states(button: Button, normal_color: Color, hover_c
|
||||
button.add_theme_stylebox_override("normal", style_normal)
|
||||
button.add_theme_stylebox_override("hover", style_hover)
|
||||
button.add_theme_stylebox_override("pressed", style_pressed)
|
||||
|
||||
func apply_button_radius(button: Button, radius: int) -> void:
|
||||
# Radius is now handled in create_button_stylebox
|
||||
# This method is kept for backward compatibility but is deprecated
|
||||
pass
|
||||
|
||||
|
||||
func apply_padding_to_stylebox(style_box: StyleBoxFlat, styles: Dictionary) -> void:
|
||||
# Apply general padding first
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class_name HTMLDiv
|
||||
extends VBoxContainer
|
||||
|
||||
func init(_element: HTMLParser.HTMLElement, _parser: HTMLParser = null):
|
||||
func init(_element: HTMLParser.HTMLElement, _parser: HTMLParser):
|
||||
pass
|
||||
|
||||
@@ -1,4 +1,75 @@
|
||||
extends VBoxContainer
|
||||
|
||||
func init(_element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
pass
|
||||
var form_element: HTMLParser.HTMLElement
|
||||
var form_parser: HTMLParser
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
form_element = element
|
||||
form_parser = parser
|
||||
|
||||
parser.register_dom_node(element, self)
|
||||
|
||||
func submit_form() -> Dictionary:
|
||||
var form_data = {}
|
||||
|
||||
if not form_element:
|
||||
return form_data
|
||||
|
||||
var form_inputs = collect_form_elements(form_element)
|
||||
|
||||
for input_element in form_inputs:
|
||||
# Use 'key' attribute as primary identifier for form field mapping
|
||||
var key_attr = input_element.get_attribute("key")
|
||||
var name_attr = input_element.get_attribute("name")
|
||||
var id_attr = input_element.get_attribute("id")
|
||||
|
||||
# Priority: key > name > id > tag_name
|
||||
var key = key_attr if not key_attr.is_empty() else name_attr if not name_attr.is_empty() else id_attr if not id_attr.is_empty() else input_element.tag_name
|
||||
|
||||
# Get the DOM node for this element
|
||||
if form_parser:
|
||||
var element_id = input_element.get_attribute("id")
|
||||
if element_id.is_empty():
|
||||
element_id = input_element.tag_name
|
||||
var dom_node = form_parser.parse_result.dom_nodes.get(element_id, null)
|
||||
if dom_node:
|
||||
var value = get_input_value(input_element.tag_name, dom_node)
|
||||
if value != null:
|
||||
form_data[key] = value
|
||||
|
||||
return form_data
|
||||
|
||||
func collect_form_elements(element: HTMLParser.HTMLElement) -> Array:
|
||||
var form_inputs = []
|
||||
|
||||
# Check if current element is an input element
|
||||
if element.tag_name in ["input", "textarea", "select"]:
|
||||
form_inputs.append(element)
|
||||
|
||||
# Recursively check children
|
||||
for child in element.children:
|
||||
form_inputs.append_array(collect_form_elements(child))
|
||||
|
||||
return form_inputs
|
||||
|
||||
func get_input_value(tag_name: String, dom_node: Node):
|
||||
match tag_name:
|
||||
"input":
|
||||
if dom_node.has_method("get_text"):
|
||||
return dom_node.get_text()
|
||||
elif dom_node.has_method("is_pressed"):
|
||||
return dom_node.is_pressed()
|
||||
elif dom_node is ColorPickerButton:
|
||||
return "#" + dom_node.color.to_html()
|
||||
elif dom_node is SpinBox:
|
||||
return dom_node.value
|
||||
elif dom_node is HSlider:
|
||||
return dom_node.value
|
||||
"textarea":
|
||||
if dom_node is TextEdit:
|
||||
return dom_node.text
|
||||
"select":
|
||||
if dom_node is OptionButton:
|
||||
return dom_node.get_item_metadata(dom_node.selected)
|
||||
|
||||
return null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends TextureRect
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, _parser: HTMLParser) -> void:
|
||||
var src = element.get_attribute("src")
|
||||
if !src: return print("Ignoring <img/> tag without \"src\" attribute.")
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ const BROWSER_TEXT: Theme = preload("res://Scenes/Styles/BrowserText.tres")
|
||||
var custom_hex_input: LineEdit
|
||||
var _file_text_content: String = ""
|
||||
var _file_binary_content: PackedByteArray = PackedByteArray()
|
||||
var _file_info: Dictionary = {}
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
var color_picker_button: ColorPickerButton = $ColorPickerButton
|
||||
var picker: ColorPicker = color_picker_button.get_picker()
|
||||
|
||||
@@ -60,7 +61,7 @@ func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
# Define which child should be active for each input type
|
||||
var active_child_map = {
|
||||
"checkbox": "CheckBox",
|
||||
"radio": "RadioButton",
|
||||
"radio": "CheckBox",
|
||||
"color": "ColorPickerButton",
|
||||
"password": "LineEdit",
|
||||
"date": "DateButton",
|
||||
@@ -72,6 +73,9 @@ func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
var active_child_name = active_child_map.get(input_type, "LineEdit")
|
||||
remove_unused_children(active_child_name)
|
||||
|
||||
if not has_node(active_child_name):
|
||||
return
|
||||
|
||||
var active_child = get_node(active_child_name)
|
||||
active_child.visible = true
|
||||
|
||||
@@ -134,6 +138,18 @@ func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
active_child.set("disabled", true)
|
||||
if element.has_attribute("readonly") and active_child.has_method("set_editable"):
|
||||
active_child.set_editable(false)
|
||||
|
||||
# Enable focus mode for text inputs to support change events on focus lost
|
||||
if active_child is LineEdit:
|
||||
active_child.focus_mode = Control.FOCUS_ALL
|
||||
|
||||
if input_type == "file":
|
||||
var file_dialog = active_child.get_node("FileDialog")
|
||||
parser.register_dom_node(element, file_dialog)
|
||||
elif input_type == "date":
|
||||
parser.register_dom_node(element, active_child)
|
||||
else:
|
||||
parser.register_dom_node(element, active_child)
|
||||
|
||||
func remove_unused_children(keep_child_name: String) -> void:
|
||||
for child in get_children():
|
||||
@@ -289,16 +305,72 @@ func _on_file_selected(path: String) -> void:
|
||||
var file_name = path.get_file()
|
||||
file_label.text = file_name
|
||||
|
||||
var file = FileAccess.open(path, FileAccess.READ)
|
||||
_process_file_data(path)
|
||||
|
||||
func _process_file_data(file_path: String) -> void:
|
||||
var file_name = file_path.get_file()
|
||||
var file_extension = file_path.get_extension().to_lower()
|
||||
var file_size = 0
|
||||
var mime_type = _get_mime_type(file_extension)
|
||||
var is_image = _is_image_file(file_extension)
|
||||
var is_text = _is_text_file(file_extension)
|
||||
|
||||
# Read file contents
|
||||
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||
if file:
|
||||
_file_text_content = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
file = FileAccess.open(path, FileAccess.READ)
|
||||
file_size = file.get_length()
|
||||
_file_binary_content = file.get_buffer(file.get_length())
|
||||
file.close()
|
||||
|
||||
# TODO: when adding Lua, make these actually usable
|
||||
|
||||
_file_info = {
|
||||
"fileName": file_name,
|
||||
"size": file_size,
|
||||
"type": mime_type,
|
||||
"binary": _file_binary_content,
|
||||
"isImage": is_image,
|
||||
"isText": is_text
|
||||
}
|
||||
|
||||
# Add text content only for text files
|
||||
if is_text:
|
||||
_file_text_content = _file_binary_content.get_string_from_utf8()
|
||||
_file_info["text"] = _file_text_content
|
||||
|
||||
# Add base64 data URL for images
|
||||
if is_image:
|
||||
var base64_data = Marshalls.raw_to_base64(_file_binary_content)
|
||||
_file_info["dataURL"] = "data:" + mime_type + ";base64," + base64_data
|
||||
|
||||
func get_file_info() -> Dictionary:
|
||||
return _file_info
|
||||
|
||||
func _get_mime_type(extension: String) -> String:
|
||||
match extension:
|
||||
"png": return "image/png"
|
||||
"jpg", "jpeg": return "image/jpeg"
|
||||
"gif": return "image/gif"
|
||||
"webp": return "image/webp"
|
||||
"svg": return "image/svg+xml"
|
||||
"bmp": return "image/bmp"
|
||||
"txt": return "text/plain"
|
||||
"html", "htm": return "text/html"
|
||||
"css": return "text/css"
|
||||
"js": return "application/javascript"
|
||||
"json": return "application/json"
|
||||
"pdf": return "application/pdf"
|
||||
"mp3": return "audio/mpeg"
|
||||
"wav": return "audio/wav"
|
||||
"ogg": return "audio/ogg"
|
||||
"mp4": return "video/mp4"
|
||||
"avi": return "video/x-msvideo"
|
||||
"mov": return "video/quicktime"
|
||||
_: return "application/octet-stream"
|
||||
|
||||
func _is_image_file(extension: String) -> bool:
|
||||
return extension in ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"]
|
||||
|
||||
func _is_text_file(extension: String) -> bool:
|
||||
return extension in ["txt", "html", "htm", "css", "js", "json", "xml", "csv", "md", "gd"]
|
||||
|
||||
func apply_input_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
if not element or not parser:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends Control
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
# This is mainly for cases where <option> appears outside of <select>
|
||||
var label = RichTextLabel.new()
|
||||
label.bbcode_enabled = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class_name HTMLP
|
||||
extends RichTextLabel
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
text = "[font_size=24]%s[/font_size]" % element.get_bbcode_formatted_text(parser)
|
||||
|
||||
# Allow mouse events to pass through to parent containers for hover effects while keeping text selection
|
||||
|
||||
@@ -2,7 +2,7 @@ extends Control
|
||||
|
||||
const BROWSER_TEXT = preload("res://Scenes/Styles/BrowserText.tres")
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
var option_button: OptionButton = $OptionButton
|
||||
|
||||
var selected_index = -1
|
||||
@@ -14,7 +14,9 @@ func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
var option_text = child_element.text_content.strip_edges()
|
||||
var option_value = child_element.get_attribute("value")
|
||||
|
||||
option_value = option_text
|
||||
# If no value attribute is specified, use the text content as the value
|
||||
if option_value.is_empty():
|
||||
option_value = option_text
|
||||
|
||||
option_button.add_item(option_text, option_index)
|
||||
option_button.set_item_metadata(option_index, option_value)
|
||||
@@ -33,3 +35,5 @@ func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
option_button.selected = selected_index
|
||||
|
||||
custom_minimum_size = option_button.size
|
||||
|
||||
parser.register_dom_node(element, option_button)
|
||||
|
||||
@@ -2,7 +2,7 @@ extends Control
|
||||
|
||||
var separator_node: Separator
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, _parser: HTMLParser) -> void:
|
||||
var direction = element.get_attribute("direction")
|
||||
|
||||
if direction == "vertical":
|
||||
|
||||
@@ -4,7 +4,7 @@ extends RichTextLabel
|
||||
@onready var rich_text_label: RichTextLabel = self
|
||||
@onready var background_rect: ColorRect = $BackgroundRect
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
text = "[font_size=24]%s[/font_size]" % element.get_bbcode_formatted_text(parser)
|
||||
|
||||
func _ready():
|
||||
|
||||
@@ -2,7 +2,7 @@ extends Control
|
||||
|
||||
const BROWSER_TEXT = preload("res://Scenes/Styles/BrowserText.tres")
|
||||
|
||||
func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
|
||||
var text_edit: TextEdit = $TextEdit
|
||||
|
||||
var placeholder = element.get_attribute("placeholder")
|
||||
@@ -10,8 +10,6 @@ func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
var rows = element.get_attribute("rows")
|
||||
var cols = element.get_attribute("cols")
|
||||
var maxlength = element.get_attribute("maxlength")
|
||||
var readonly = element.get_attribute("readonly")
|
||||
var disabled = element.get_attribute("disabled")
|
||||
|
||||
# Set placeholder text
|
||||
text_edit.placeholder_text = placeholder
|
||||
@@ -70,6 +68,11 @@ func init(element: HTMLParser.HTMLElement, _parser: HTMLParser = null) -> void:
|
||||
if text_edit.text_changed.is_connected(_on_text_changed):
|
||||
text_edit.text_changed.disconnect(_on_text_changed)
|
||||
text_edit.text_changed.connect(_on_text_changed.bind(max_len))
|
||||
|
||||
# Enable focus mode for textarea to support change events on focus lost
|
||||
text_edit.focus_mode = Control.FOCUS_ALL
|
||||
|
||||
parser.register_dom_node(element, text_edit)
|
||||
|
||||
func _on_text_changed(max_length: int) -> void:
|
||||
var text_edit = $TextEdit as TextEdit
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
@@ -128,7 +128,9 @@ func render() -> void:
|
||||
for inline_element in inline_elements:
|
||||
var inline_node = await create_element_node(inline_element, parser)
|
||||
if inline_node:
|
||||
parser.register_dom_node(inline_element, inline_node)
|
||||
# Input elements register their own DOM nodes in their init() function
|
||||
if inline_element.tag_name not in ["input", "textarea", "select", "button"]:
|
||||
parser.register_dom_node(inline_element, inline_node)
|
||||
|
||||
safe_add_child(hbox, inline_node)
|
||||
# Handle hyperlinks for all inline elements
|
||||
@@ -142,7 +144,9 @@ func render() -> void:
|
||||
|
||||
var element_node = await create_element_node(element, parser)
|
||||
if element_node:
|
||||
parser.register_dom_node(element, element_node)
|
||||
# Input elements register their own DOM nodes in their init() function
|
||||
if element.tag_name not in ["input", "textarea", "select", "button"]:
|
||||
parser.register_dom_node(element, element_node)
|
||||
|
||||
# ul/ol handle their own adding
|
||||
if element.tag_name != "ul" and element.tag_name != "ol":
|
||||
@@ -277,21 +281,23 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
|
||||
if not child_element.is_inline_element() or is_flex_container:
|
||||
var child_node = await create_element_node(child_element, parser)
|
||||
if child_node and is_instance_valid(container_for_children):
|
||||
parser.register_dom_node(child_element, child_node)
|
||||
# Input elements register their own DOM nodes in their init() function
|
||||
if child_element.tag_name not in ["input", "textarea", "select", "button"]:
|
||||
parser.register_dom_node(child_element, child_node)
|
||||
safe_add_child(container_for_children, child_node)
|
||||
|
||||
return final_node
|
||||
|
||||
func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLParser = null) -> Control:
|
||||
func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLParser) -> Control:
|
||||
var node: Control = null
|
||||
|
||||
match element.tag_name:
|
||||
"p":
|
||||
node = P.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"pre":
|
||||
node = PRE.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"h1", "h2", "h3", "h4", "h5", "h6":
|
||||
match element.tag_name:
|
||||
"h1": node = H1.instantiate()
|
||||
@@ -300,16 +306,16 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
|
||||
"h4": node = H4.instantiate()
|
||||
"h5": node = H5.instantiate()
|
||||
"h6": node = H6.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"br":
|
||||
node = BR.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"img":
|
||||
node = IMG.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"separator":
|
||||
node = SEPARATOR.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"form":
|
||||
var form_styles = parser.get_element_styles_with_inheritance(element, "", [])
|
||||
var is_flex_form = form_styles.has("display") and ("flex" in form_styles["display"])
|
||||
@@ -319,7 +325,7 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
|
||||
return null
|
||||
else:
|
||||
node = FORM.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
|
||||
# Manually process children for non-flex forms
|
||||
for child_element in element.children:
|
||||
@@ -350,13 +356,13 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
|
||||
node.init(element)
|
||||
"select":
|
||||
node = SELECT.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"option":
|
||||
node = OPTION.instantiate()
|
||||
node.init(element, parser)
|
||||
"textarea":
|
||||
node = TEXTAREA.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
"div":
|
||||
var styles = parser.get_element_styles_with_inheritance(element, "", [])
|
||||
var hover_styles = parser.get_element_styles_with_inheritance(element, "hover", [])
|
||||
@@ -366,13 +372,13 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
|
||||
node = BackgroundUtils.create_panel_container_with_background(styles, hover_styles)
|
||||
else:
|
||||
node = DIV.instantiate()
|
||||
node.init(element)
|
||||
node.init(element, parser)
|
||||
|
||||
var has_only_text = is_text_only_element(element)
|
||||
|
||||
if has_only_text:
|
||||
var p_node = P.instantiate()
|
||||
p_node.init(element)
|
||||
p_node.init(element, parser)
|
||||
|
||||
var container_for_children = node
|
||||
if node is PanelContainer and node.get_child_count() > 0:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class_name DateButton
|
||||
extends Button
|
||||
|
||||
signal date_selected(date_text: String)
|
||||
|
||||
var calendar: Calendar
|
||||
var calendar_control: Control
|
||||
|
||||
@@ -54,11 +56,19 @@ func parse_date_string(date_string: String) -> Dictionary:
|
||||
|
||||
func update_button_text() -> void:
|
||||
var date = calendar.selected
|
||||
text = "%d/%d/%d" % [date.month, date.day, date.year]
|
||||
if date and date.has("month") and date.has("day") and date.has("year"):
|
||||
text = "%02d/%02d/%04d" % [date.month, date.day, date.year]
|
||||
date_selected.emit(text)
|
||||
else:
|
||||
text = "mm/dd/yyyy"
|
||||
|
||||
func _on_date_selected():
|
||||
var date = calendar.selected
|
||||
text = "%02d/%02d/%04d" % [date.month, date.day, date.year]
|
||||
if date and date.has("month") and date.has("day") and date.has("year"):
|
||||
text = "%02d/%02d/%04d" % [date.month, date.day, date.year]
|
||||
date_selected.emit(text)
|
||||
else:
|
||||
text = "mm/dd/yyyy"
|
||||
|
||||
func _on_button_pressed():
|
||||
if calendar.is_visible():
|
||||
|
||||
Reference in New Issue
Block a user