user domains API, .value on input, fix flex sizing

This commit is contained in:
Face
2025-08-19 18:27:25 +03:00
parent dacda095d5
commit 99f17dc42c
18 changed files with 705 additions and 356 deletions

View File

@@ -88,7 +88,7 @@ func _resort() -> void:
if not auto_size_width:
available_width = calculate_available_dimension(true)
elif flex_wrap == FlexContainer.FlexWrap.Wrap:
elif flex_wrap != FlexContainer.FlexWrap.NoWrap:
available_width = get_parent_or_fallback_size(true)
if not auto_size_height:

View File

@@ -170,7 +170,7 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = "
styles[property] = inline_parsed[property]
# Inherit certain properties from parent elements
var inheritable_properties = ["width", "height", "font-size", "color", "font-family", "cursor", "font-bold", "font-italic", "underline"]
var inheritable_properties = ["font-size", "color", "font-family", "cursor", "font-bold", "font-italic", "underline"]
var parent_element = element.parent
while parent_element:
var parent_styles = get_element_styles_internal(parent_element, event)

View File

@@ -663,10 +663,7 @@ func _handle_text_setting(operation: Dictionary):
var text_node = get_dom_node(dom_node, "text")
if text_node:
if text_node is RichTextLabel:
var formatted_text = element.get_bbcode_formatted_text(dom_parser)
formatted_text = "[font_size=24]%s[/font_size]" % formatted_text
text_node.text = formatted_text
StyleManager.apply_styles_to_label(text_node, dom_parser.get_element_styles_with_inheritance(element, "", []), element, dom_parser, text)
text_node.call_deferred("_auto_resize_to_content")
elif text_node.has_method("set_text"):
text_node.set_text(text)
@@ -677,10 +674,7 @@ func _handle_text_setting(operation: Dictionary):
else:
var rich_text_label = _find_rich_text_label_recursive(dom_node)
if rich_text_label:
var formatted_text = element.get_bbcode_formatted_text(dom_parser)
formatted_text = "[font_size=24]%s[/font_size]" % formatted_text
rich_text_label.text = formatted_text
StyleManager.apply_styles_to_label(rich_text_label, dom_parser.get_element_styles_with_inheritance(element, "", []), element, dom_parser, text)
rich_text_label.call_deferred("_auto_resize_to_content")
func _find_rich_text_label_recursive(node: Node) -> RichTextLabel:

View File

@@ -60,55 +60,24 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
if styles.has("height"):
height = parse_size(styles["height"])
# Skip width/height inheritance for buttons when inheriting from auto-sized containers
var skip_sizing = SizingUtils.should_skip_sizing(node, element, parser)
if (width != null or height != null) and not skip_sizing:
# FlexContainers handle percentage sizing differently than regular controls
if node is FlexContainer:
if width != null:
if SizingUtils.is_percentage(width):
# For FlexContainers with percentage width, use proportion sizing
var percentage_value = float(width.replace("%", "")) / 100.0
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
node.size_flags_stretch_ratio = percentage_value
else:
node.custom_minimum_size.x = width
node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
node.set_meta("size_flags_horizontal_set", true)
if height != null:
if SizingUtils.is_percentage(height):
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
else:
node.custom_minimum_size.y = height
node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
node.set_meta("size_flags_vertical_set", true)
node.set_meta("size_flags_set_by_style_manager", true)
elif node is VBoxContainer or node is HBoxContainer or node is Container:
# Hcontainer nodes (like ul, ol)
SizingUtils.apply_container_dimension_sizing(node, width, height, styles)
elif node is HTMLP:
# Only apply sizing if element has explicit size, otherwise preserve natural sizing
var element_styles = parser.get_element_styles_internal(element, "")
if element_styles.has("width") or element_styles.has("height"):
var orig_h_flag = node.size_flags_horizontal
var orig_v_flag = node.size_flags_vertical
SizingUtils.apply_regular_control_sizing(node, width, height, styles)
if not element_styles.has("width"):
node.size_flags_horizontal = orig_h_flag
if not element_styles.has("height"):
node.size_flags_vertical = orig_v_flag
else:
if element.tag_name == "img" and SizingUtils.is_percentage(width) and SizingUtils.is_percentage(height):
if width != null:
if width is String and width == "100%":
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
# Clear any hardcoded sizing
node.custom_minimum_size = Vector2.ZERO
node.custom_minimum_size.x = 0
else:
# regular controls
SizingUtils.apply_regular_control_sizing(node, width, height, styles)
node.custom_minimum_size.x = width
node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
if height != null:
if height is String and height == "100%":
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
node.custom_minimum_size.y = 0
else:
node.custom_minimum_size.y = height
node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
apply_element_centering(node, styles)
@@ -539,7 +508,7 @@ static func apply_body_styles(body: HTMLParser.HTMLElement, parser: HTMLParser,
original_parent.move_child(margin_container, container_index)
margin_container.add_child(website_container)
var padding_val = parse_size(styles["padding"])
var padding_val = parse_size(styles["padding"] if styles.has("padding") else 0)
margin_container.add_theme_constant_override("margin_left", padding_val)
margin_container.add_theme_constant_override("margin_right", padding_val)

View File

@@ -61,7 +61,7 @@ static func apply_flex_container_properties(node, styles: Dictionary) -> void:
if styles.has("width"):
var width_val = styles["width"]
if width_val == "full":
if width_val == "full" or width_val == "100%":
# For flex containers, w-full should expand to fill parent
node.set_meta("should_fill_horizontal", true)
elif typeof(width_val) == TYPE_STRING and width_val.ends_with("%"):
@@ -79,6 +79,7 @@ static func apply_flex_container_properties(node, styles: Dictionary) -> void:
node.set_meta("custom_css_height", SizingUtils.parse_size_value(height_val))
if styles.has("background-color"):
node.set_meta("custom_css_background_color", styles["background-color"])
node.update_layout()
static func apply_flex_item_properties(node: Control, styles: Dictionary) -> void:

View File

@@ -290,6 +290,84 @@ static func render_new_element(element: HTMLParser.HTMLElement, parent_node: Nod
main_scene.safe_add_child(container_node, element_node)
static func _get_input_value(element: HTMLParser.HTMLElement, dom_node: Node) -> Variant:
var input_type = element.get_attribute("type").to_lower()
match input_type:
"checkbox", "radio":
if dom_node is CheckBox:
return dom_node.button_pressed
return false
"color":
if dom_node is ColorPickerButton:
return "#" + dom_node.color.to_html(false)
return "#ffffff"
"range":
if dom_node is HSlider:
return dom_node.value
return 0.0
"number":
if dom_node is SpinBox:
return dom_node.value
return 0.0
"file":
# For file inputs, need to find the input control that has get_file_info method
var input_control = _find_input_control_with_file_info(dom_node)
if input_control:
var file_info = input_control.get_file_info()
return file_info.get("fileName", "")
return ""
"date":
if dom_node is DateButton:
if dom_node.has_method("get_date_text"):
return dom_node.get_date_text()
return ""
_: # text, password, email, etc.
if dom_node is LineEdit:
return dom_node.text
elif dom_node is TextEdit:
return dom_node.text
return ""
static func _find_input_control_with_file_info(node: Node) -> Node:
if node and node.has_method("get_file_info"):
return node
var current = node
while current:
current = current.get_parent()
if current and current.has_method("get_file_info"):
return current
return null
static func _set_input_value(element: HTMLParser.HTMLElement, dom_node: Node, value: Variant) -> void:
var input_type = element.get_attribute("type").to_lower()
match input_type:
"checkbox", "radio":
if dom_node is CheckBox:
dom_node.button_pressed = bool(value)
"color":
if dom_node is ColorPickerButton:
var color_value = str(value)
if color_value.begins_with("#"):
dom_node.color = Color.from_string(color_value, Color.WHITE)
"range":
if dom_node is HSlider:
dom_node.value = float(value)
"number":
if dom_node is SpinBox:
dom_node.value = float(value)
"date":
if dom_node is DateButton and dom_node.has_method("set_date_from_string"):
dom_node.set_date_from_string(str(value))
_: # text, password, email, etc.
if dom_node is LineEdit:
dom_node.text = str(value)
elif dom_node is TextEdit:
dom_node.text = str(value)
# Helper functions
static func find_element_by_id(element_id: String, dom_parser: HTMLParser) -> HTMLParser.HTMLElement:
if element_id == "body":
@@ -852,6 +930,23 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
return LuaAudioUtils.handle_dom_audio_index(vm, element_id, key)
match key:
"value":
if lua_api and tag_name == "input":
vm.lua_getfield(1, "_element_id")
var element_id: String = vm.lua_tostring(-1)
vm.lua_pop(1)
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
var dom_node = lua_api.dom_parser.parse_result.dom_nodes.get(element_id, null)
if element and dom_node:
var input_value = _get_input_value(element, dom_node)
vm.lua_pushstring(str(input_value))
return 1
# Fallback to empty string
vm.lua_pushstring("")
return 1
"text":
if lua_api:
# Get element ID and find the element
@@ -929,12 +1024,10 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
return 1
static func _add_classlist_support(vm: LuauVM, lua_api: LuaAPI) -> void:
# Create classList table with threaded methods
vm.lua_newtable()
# Store the element_id in the classList table
vm.lua_getfield(-2, "_element_id") # Get element_id from parent element
vm.lua_setfield(-2, "_element_id") # Store it in classList table
vm.lua_getfield(-2, "_element_id")
vm.lua_setfield(-2, "_element_id")
# Add classList methods
vm.lua_pushcallable(LuaDOMUtils._classlist_add_wrapper, "classList.add")
@@ -961,12 +1054,11 @@ static func _add_classlist_support(vm: LuauVM, lua_api: LuaAPI) -> void:
vm.lua_setfield(-2, "classList")
static func _classlist_add_wrapper(vm: LuauVM) -> int:
# Get lua_api from VM metadata
var lua_api = vm.get_meta("lua_api") as LuaAPI
if not lua_api:
return 0
vm.luaL_checktype(1, vm.LUA_TTABLE) # classList table
vm.luaL_checktype(1, vm.LUA_TTABLE)
var cls: String = vm.luaL_checkstring(2)
# Get element_id from classList table
@@ -1132,6 +1224,21 @@ static func _element_newindex_wrapper(vm: LuauVM) -> int:
return LuaAudioUtils.handle_dom_audio_newindex(vm, element_id, key, value)
match key:
"value":
if tag_name == "input":
vm.lua_getfield(1, "_element_id")
var element_id: String = vm.lua_tostring(-1)
vm.lua_pop(1)
var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
var dom_node = lua_api.dom_parser.parse_result.dom_nodes.get(element_id, null)
if element and dom_node:
# Update the HTML element's value attribute
element.set_attribute("value", str(value))
_set_input_value(element, dom_node, value)
return 0
"text":
var text: String = str(value) # Convert value to string

View File

@@ -53,18 +53,6 @@ func _ready():
ProjectSettings.set_setting("display/window/size/min_width", MIN_SIZE.x)
ProjectSettings.set_setting("display/window/size/min_height", MIN_SIZE.y)
DisplayServer.window_set_min_size(MIN_SIZE)
get_viewport().size_changed.connect(_on_viewport_size_changed)
func _on_viewport_size_changed():
recalculate_percentage_elements(website_container)
func recalculate_percentage_elements(node: Node):
if node is Control and node.has_meta("needs_percentage_recalc"):
SizingUtils.apply_container_percentage_sizing(node)
for child in node.get_children():
recalculate_percentage_elements(child)
var current_domain = "" # Store current domain for display
@@ -267,9 +255,6 @@ static func safe_add_child(parent: Node, child: Node) -> void:
child.get_parent().remove_child(child)
parent.add_child(child)
if child.has_meta("container_percentage_width") or child.has_meta("container_percentage_height"):
SizingUtils.apply_container_percentage_sizing(child)
child.set_meta("needs_percentage_recalc", true)
func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
if element.tag_name == "a":
@@ -290,6 +275,7 @@ func is_text_only_element(element: HTMLParser.HTMLElement) -> bool:
func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) -> Control:
var styles = parser.get_element_styles_with_inheritance(element, "", [])
var hover_styles = parser.get_element_styles_with_inheritance(element, "hover", [])
var is_flex_container = styles.has("display") and ("flex" in styles["display"])
var final_node: Control
@@ -308,10 +294,22 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
if is_flex_container:
# The element's primary identity IS a flex container.
# We create it directly.
final_node = AUTO_SIZING_FLEX_CONTAINER.new()
final_node.name = "Flex_" + element.tag_name
container_for_children = final_node
if element.tag_name == "div":
if BackgroundUtils.needs_background_wrapper(styles) or hover_styles.size() > 0:
final_node = BackgroundUtils.create_panel_container_with_background(styles, hover_styles)
var flex_container = AUTO_SIZING_FLEX_CONTAINER.new()
flex_container.name = "Flex_" + element.tag_name
var vbox = final_node.get_child(0) as VBoxContainer
vbox.add_child(flex_container)
container_for_children = flex_container
else:
final_node = AUTO_SIZING_FLEX_CONTAINER.new()
final_node.name = "Flex_" + element.tag_name
container_for_children = final_node
else:
final_node = AUTO_SIZING_FLEX_CONTAINER.new()
final_node.name = "Flex_" + element.tag_name
container_for_children = final_node
# For FLEX ul/ol elements, we need to create the li children directly in the flex container
if element.tag_name == "ul" or element.tag_name == "ol":
@@ -353,11 +351,20 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
# Apply flex CONTAINER properties if it's a flex container
if is_flex_container:
var flex_container_node = final_node
# If the node was wrapped in a MarginContainer, get the inner FlexContainer
if final_node is MarginContainer and final_node.get_child_count() > 0:
if final_node is FlexContainer:
# Direct FlexContainer
flex_container_node = final_node
elif final_node is MarginContainer and final_node.get_child_count() > 0:
var first_child = final_node.get_child(0)
if first_child is FlexContainer:
flex_container_node = first_child
elif final_node is PanelContainer and final_node.get_child_count() > 0:
var vbox = final_node.get_child(0)
if vbox is VBoxContainer and vbox.get_child_count() > 0:
var potential_flex = vbox.get_child(0)
if potential_flex is FlexContainer:
flex_container_node = potential_flex
if flex_container_node is FlexContainer:
FlexUtils.apply_flex_container_properties(flex_container_node, styles)
@@ -476,7 +483,7 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
var hover_styles = parser.get_element_styles_with_inheritance(element, "hover", [])
var is_flex_container = styles.has("display") and ("flex" in styles["display"])
# For flex divs, don't create div scene - the AutoSizingFlexContainer handles it
# For flex divs, let the general flex container logic handle them
if is_flex_container:
return null