show(), hide(), visible. font-light, normal, medium, semibold, bold, extrabold, black. protocol connection pooling. fetch() with GURT. DNS from HTTP to GURT.
This commit is contained in:
@@ -462,9 +462,33 @@ static func parse_utility_class_internal(rule: CSSRule, utility_name: String) ->
|
||||
return
|
||||
|
||||
# Handle font weight
|
||||
if utility_name == "font-thin":
|
||||
rule.properties["font-thin"] = true
|
||||
return
|
||||
if utility_name == "font-extralight":
|
||||
rule.properties["font-extralight"] = true
|
||||
return
|
||||
if utility_name == "font-light":
|
||||
rule.properties["font-light"] = true
|
||||
return
|
||||
if utility_name == "font-normal":
|
||||
rule.properties["font-normal"] = true
|
||||
return
|
||||
if utility_name == "font-medium":
|
||||
rule.properties["font-medium"] = true
|
||||
return
|
||||
if utility_name == "font-semibold":
|
||||
rule.properties["font-semibold"] = true
|
||||
return
|
||||
if utility_name == "font-bold":
|
||||
rule.properties["font-bold"] = true
|
||||
return
|
||||
if utility_name == "font-extrabold":
|
||||
rule.properties["font-extrabold"] = true
|
||||
return
|
||||
if utility_name == "font-black":
|
||||
rule.properties["font-black"] = true
|
||||
return
|
||||
|
||||
# Handle font family
|
||||
if utility_name == "font-sans":
|
||||
@@ -478,7 +502,7 @@ static func parse_utility_class_internal(rule: CSSRule, utility_name: String) ->
|
||||
rule.properties["font-mono"] = true
|
||||
return
|
||||
|
||||
var reserved_font_styles = ["font-sans", "font-serif", "font-mono", "font-bold", "font-italic"]
|
||||
var reserved_font_styles = ["font-sans", "font-serif", "font-mono", "font-thin", "font-extralight", "font-light", "font-normal", "font-medium", "font-semibold", "font-bold", "font-extrabold", "font-black", "font-italic"]
|
||||
# Handle custom font families like font-roboto
|
||||
if utility_name.begins_with("font-") and not utility_name in reserved_font_styles:
|
||||
var font_name = utility_name.substr(5) # after 'font-'
|
||||
@@ -521,8 +545,8 @@ static func parse_utility_class_internal(rule: CSSRule, utility_name: String) ->
|
||||
val = val.substr(1, val.length() - 2)
|
||||
rule.properties["width"] = SizeUtils.parse_size(val)
|
||||
return
|
||||
# Height
|
||||
if utility_name.begins_with("h-"):
|
||||
# Height, but h-full is temporarily disabled since it fucks with Yoga layout engine
|
||||
if utility_name.begins_with("h-") and utility_name != "h-full":
|
||||
var val = utility_name.substr(2)
|
||||
if val.begins_with("[") and val.ends_with("]"):
|
||||
val = val.substr(1, val.length() - 2)
|
||||
|
||||
@@ -412,47 +412,69 @@ func apply_element_styles(node: Control, element: HTMLElement, parser: HTMLParse
|
||||
label.text = text
|
||||
|
||||
static func apply_element_bbcode_formatting(element: HTMLElement, styles: Dictionary, content: String, parser: HTMLParser = null) -> String:
|
||||
# Apply general styling first (color, font-weight) for all elements
|
||||
var formatted_content = content
|
||||
|
||||
# Apply font weight (bold/semibold/etc)
|
||||
if styles.has("font-bold") and styles["font-bold"]:
|
||||
formatted_content = "[b]" + formatted_content + "[/b]"
|
||||
elif styles.has("font-semibold") and styles["font-semibold"]:
|
||||
formatted_content = "[b]" + formatted_content + "[/b]" # BBCode doesn't have semibold, use bold
|
||||
|
||||
# Apply italic
|
||||
if styles.has("font-italic") and styles["font-italic"]:
|
||||
formatted_content = "[i]" + formatted_content + "[/i]"
|
||||
|
||||
# Apply underline
|
||||
if styles.has("underline") and styles["underline"]:
|
||||
formatted_content = "[u]" + formatted_content + "[/u]"
|
||||
|
||||
# Apply color
|
||||
if styles.has("color"):
|
||||
var color = styles["color"]
|
||||
if typeof(color) == TYPE_COLOR:
|
||||
color = "#" + color.to_html(false)
|
||||
else:
|
||||
color = str(color)
|
||||
formatted_content = "[color=%s]%s[/color]" % [color, formatted_content]
|
||||
|
||||
# Apply tag-specific formatting
|
||||
match element.tag_name:
|
||||
"b":
|
||||
if styles.has("font-bold") and styles["font-bold"]:
|
||||
return "[b]" + content + "[/b]"
|
||||
if not (styles.has("font-bold") and styles["font-bold"]):
|
||||
formatted_content = "[b]" + formatted_content + "[/b]"
|
||||
"i":
|
||||
if styles.has("font-italic") and styles["font-italic"]:
|
||||
return "[i]" + content + "[/i]"
|
||||
if not (styles.has("font-italic") and styles["font-italic"]):
|
||||
formatted_content = "[i]" + formatted_content + "[/i]"
|
||||
"u":
|
||||
if styles.has("underline") and styles["underline"]:
|
||||
return "[u]" + content + "[/u]"
|
||||
if not (styles.has("underline") and styles["underline"]):
|
||||
formatted_content = "[u]" + formatted_content + "[/u]"
|
||||
"small":
|
||||
if styles.has("font-size"):
|
||||
return "[font_size=%d]%s[/font_size]" % [styles["font-size"], content]
|
||||
formatted_content = "[font_size=%d]%s[/font_size]" % [styles["font-size"], formatted_content]
|
||||
else:
|
||||
return "[font_size=20]%s[/font_size]" % content
|
||||
formatted_content = "[font_size=20]%s[/font_size]" % formatted_content
|
||||
"mark":
|
||||
if styles.has("bg"):
|
||||
var color = styles["bg"]
|
||||
if typeof(color) == TYPE_COLOR:
|
||||
color = color.to_html(false)
|
||||
return "[bgcolor=#%s]%s[/bgcolor]" % [color, content]
|
||||
var bg_color = styles["bg"]
|
||||
if typeof(bg_color) == TYPE_COLOR:
|
||||
bg_color = bg_color.to_html(false)
|
||||
formatted_content = "[bgcolor=#%s]%s[/bgcolor]" % [bg_color, formatted_content]
|
||||
else:
|
||||
return "[bgcolor=#FFFF00]%s[/bgcolor]" % content
|
||||
formatted_content = "[bgcolor=#FFFF00]%s[/bgcolor]" % formatted_content
|
||||
"code":
|
||||
if styles.has("font-size"):
|
||||
return "[font_size=%d][code]%s[/code][/font_size]" % [styles["font-size"], content]
|
||||
formatted_content = "[font_size=%d][code]%s[/code][/font_size]" % [styles["font-size"], formatted_content]
|
||||
else:
|
||||
return "[font_size=20][code]%s[/code][/font_size]" % content
|
||||
formatted_content = "[font_size=20][code]%s[/code][/font_size]" % formatted_content
|
||||
"a":
|
||||
var href = element.get_attribute("href")
|
||||
var color = "#1a0dab"
|
||||
if styles.has("color"):
|
||||
var c = styles["color"]
|
||||
if typeof(c) == TYPE_COLOR:
|
||||
color = "#" + c.to_html(false)
|
||||
else:
|
||||
color = str(c)
|
||||
|
||||
if href.length() > 0:
|
||||
# Pass raw href - URL resolution happens in handle_link_click
|
||||
return "[color=%s][url=%s]%s[/url][/color]" % [color, href, content]
|
||||
return content
|
||||
formatted_content = "[url=%s]%s[/url]" % [href, formatted_content]
|
||||
|
||||
return formatted_content
|
||||
|
||||
static func get_bbcode_with_styles(element: HTMLElement, styles: Dictionary, parser: HTMLParser) -> String:
|
||||
var text = ""
|
||||
|
||||
@@ -661,18 +661,43 @@ func _handle_text_setting(operation: Dictionary):
|
||||
if dom_node:
|
||||
var text_node = get_dom_node(dom_node, "text")
|
||||
if text_node:
|
||||
if text_node.has_method("set_text"):
|
||||
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
|
||||
text_node.call_deferred("_auto_resize_to_content")
|
||||
elif text_node.has_method("set_text"):
|
||||
text_node.set_text(text)
|
||||
elif "text" in text_node:
|
||||
text_node.text = text
|
||||
if text_node.has_method("_auto_resize_to_content"):
|
||||
text_node.call_deferred("_auto_resize_to_content")
|
||||
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
|
||||
rich_text_label.call_deferred("_auto_resize_to_content")
|
||||
|
||||
func _find_rich_text_label_recursive(node: Node) -> RichTextLabel:
|
||||
if node is RichTextLabel:
|
||||
return node
|
||||
|
||||
for child in node.get_children():
|
||||
var result = _find_rich_text_label_recursive(child)
|
||||
if result:
|
||||
return result
|
||||
|
||||
return null
|
||||
|
||||
func _handle_text_getting(operation: Dictionary):
|
||||
var selector: String = operation.selector
|
||||
|
||||
var element = SelectorUtils.find_first_matching(selector, dom_parser.parse_result.all_elements)
|
||||
if element:
|
||||
# Return the element's cached text content from the HTML element
|
||||
# This avoids the need for a callback system since we have the text cached
|
||||
return element.text_content
|
||||
return ""
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const SECONDARY_COLOR = Color(43/255.0, 43/255.0, 43/255.0, 1)
|
||||
const HOVER_COLOR = Color(0, 0, 0, 1)
|
||||
|
||||
const DEFAULT_CSS = """
|
||||
body { text-base text-[#000000] text-left bg-white }
|
||||
body { text-base text-[#000000] text-left bg-white font-serif }
|
||||
h1 { text-5xl font-bold }
|
||||
h2 { text-4xl font-bold }
|
||||
h3 { text-3xl font-bold }
|
||||
@@ -21,7 +21,7 @@ code { text-xl font-mono }
|
||||
a { text-[#1a0dab] }
|
||||
pre { text-xl font-mono }
|
||||
|
||||
button { text-[16px] bg-[#1b1b1b] rounded-md text-white hover:bg-[#2a2a2a] active:bg-[#101010] }
|
||||
button { text-[16px] bg-[#1b1b1b] rounded-md text-white hover:bg-[#2a2a2a] active:bg-[#101010] px-3 py-1.5 }
|
||||
button[disabled] { bg-[#666666] text-[#999999] cursor-not-allowed }
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extends RefCounted
|
||||
class_name GurtProtocol
|
||||
|
||||
const DNS_API_URL = "http://localhost:8080"
|
||||
const DNS_API_URL = "gurt://localhost:8877"
|
||||
|
||||
static func is_gurt_domain(url: String) -> bool:
|
||||
if url.begins_with("gurt://"):
|
||||
@@ -52,41 +52,39 @@ static func is_ip_address(address: String) -> bool:
|
||||
return true
|
||||
|
||||
static func fetch_domain_info(name: String, tld: String) -> Dictionary:
|
||||
var http_request = HTTPRequest.new()
|
||||
var tree = Engine.get_main_loop()
|
||||
tree.current_scene.add_child(http_request)
|
||||
var path = "/domain/" + name + "/" + tld
|
||||
var dns_address = "localhost:8877"
|
||||
|
||||
http_request.timeout = 5.0
|
||||
print("DNS API URL: gurt://" + dns_address + path)
|
||||
|
||||
var url = DNS_API_URL + "/domain/" + name + "/" + tld
|
||||
print("DNS API URL: ", url)
|
||||
var response = await fetch_content_via_gurt_direct(dns_address, path)
|
||||
|
||||
var error = http_request.request(url)
|
||||
if response.has("error"):
|
||||
if "No response from GURT server" in response.error or "Failed to create GURT client" in response.error:
|
||||
return {"error": "DNS server is not responding"}
|
||||
else:
|
||||
return {"error": "Failed to make DNS request"}
|
||||
|
||||
if error != OK:
|
||||
print("HTTP request failed with error: ", error)
|
||||
http_request.queue_free()
|
||||
return {"error": "Failed to make DNS request"}
|
||||
|
||||
var response = await http_request.request_completed
|
||||
http_request.queue_free()
|
||||
|
||||
if response[1] == 0 and response[3].size() == 0:
|
||||
if not response.has("content"):
|
||||
return {"error": "DNS server is not responding"}
|
||||
|
||||
var http_code = response[1]
|
||||
var body = response[3]
|
||||
|
||||
if http_code != 200:
|
||||
return {"error": "Domain not found or not approved"}
|
||||
var content = response.content
|
||||
if content.is_empty():
|
||||
return {"error": "DNS server is not responding"}
|
||||
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(body.get_string_from_utf8())
|
||||
var parse_result = json.parse(content.get_string_from_utf8())
|
||||
|
||||
if parse_result != OK:
|
||||
return {"error": "Invalid JSON response from DNS server"}
|
||||
|
||||
return json.data
|
||||
var data = json.data
|
||||
|
||||
# Check if the response indicates an error (like 404)
|
||||
if data is Dictionary and data.has("error"):
|
||||
return {"error": "Domain not found or not approved"}
|
||||
|
||||
return data
|
||||
|
||||
static func fetch_content_via_gurt(ip: String, path: String = "/") -> Dictionary:
|
||||
var client = GurtProtocolClient.new()
|
||||
|
||||
@@ -62,16 +62,16 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
|
||||
node.size_flags_stretch_ratio = percentage_value
|
||||
else:
|
||||
node.custom_minimum_size.x = width
|
||||
var should_center_h = styles.has("mx-auto") or styles.has("justify-self-center") or (styles.has("text-align") and styles["text-align"] == "center")
|
||||
node.size_flags_horizontal = Control.SIZE_SHRINK_CENTER if should_center_h else Control.SIZE_SHRINK_BEGIN
|
||||
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
|
||||
var should_center_v = styles.has("my-auto") or styles.has("align-self-center")
|
||||
node.size_flags_vertical = Control.SIZE_SHRINK_CENTER if should_center_v else Control.SIZE_SHRINK_BEGIN
|
||||
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:
|
||||
@@ -97,6 +97,9 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
|
||||
else:
|
||||
# regular controls
|
||||
SizingUtils.apply_regular_control_sizing(node, width, height, styles)
|
||||
|
||||
# Apply centering for FlexContainers
|
||||
apply_flexcontainer_centering(node, styles)
|
||||
|
||||
if label and label != node:
|
||||
label.anchors_preset = Control.PRESET_FULL_RECT
|
||||
@@ -145,7 +148,7 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
|
||||
if needs_styling:
|
||||
# If node is a MarginContainer wrapper, get the actual content node for styling
|
||||
var content_node = node
|
||||
if node is MarginContainer and node.name.begins_with("MarginWrapper_"):
|
||||
if node is MarginContainer and node.has_meta("is_margin_wrapper"):
|
||||
if node.get_child_count() > 0:
|
||||
content_node = node.get_child(0)
|
||||
|
||||
@@ -168,7 +171,7 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
|
||||
target_node_for_bg.call_deferred("add_background_rect")
|
||||
else:
|
||||
var content_node = node
|
||||
if node is MarginContainer and node.name.begins_with("MarginWrapper_"):
|
||||
if node is MarginContainer and node.has_meta("is_margin_wrapper"):
|
||||
if node.get_child_count() > 0:
|
||||
content_node = node.get_child(0)
|
||||
|
||||
@@ -195,6 +198,7 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
|
||||
|
||||
apply_transform_properties(transform_target, styles)
|
||||
|
||||
|
||||
return node
|
||||
|
||||
static func apply_stylebox_to_panel_container(panel_container: PanelContainer, styles: Dictionary) -> void:
|
||||
@@ -273,12 +277,12 @@ static func clear_styling_metadata(node: Control) -> void:
|
||||
static func handle_margin_wrapper(node: Control, styles: Dictionary, needs_margin: bool):
|
||||
var current_wrapper = null
|
||||
|
||||
if node is MarginContainer and node.name.begins_with("MarginWrapper_"):
|
||||
if node is MarginContainer and node.has_meta("is_margin_wrapper"):
|
||||
current_wrapper = node
|
||||
|
||||
elif node.get_parent() and node.get_parent() is MarginContainer:
|
||||
var parent = node.get_parent()
|
||||
if parent.name.begins_with("MarginWrapper_"):
|
||||
if parent.has_meta("is_margin_wrapper"):
|
||||
current_wrapper = parent
|
||||
|
||||
if needs_margin:
|
||||
@@ -323,6 +327,7 @@ static func remove_margin_wrapper(margin_container: MarginContainer, original_no
|
||||
static func apply_margin_wrapper(node: Control, styles: Dictionary) -> Control:
|
||||
var margin_container = MarginContainer.new()
|
||||
margin_container.name = "MarginWrapper_" + node.name
|
||||
margin_container.set_meta("is_margin_wrapper", true)
|
||||
|
||||
var has_explicit_width = styles.has("width")
|
||||
var has_explicit_height = styles.has("height")
|
||||
@@ -406,11 +411,16 @@ static func apply_styles_to_label(label: Control, styles: Dictionary, element: H
|
||||
if not FontManager.loaded_fonts.has(font_family):
|
||||
# Font not loaded yet, use sans-serif as fallback
|
||||
var fallback_font = FontManager.get_font("sans-serif")
|
||||
apply_font_to_label(label, fallback_font)
|
||||
apply_font_to_label(label, fallback_font, styles)
|
||||
|
||||
if font_resource:
|
||||
apply_font_to_label(label, font_resource)
|
||||
|
||||
apply_font_to_label(label, font_resource, styles)
|
||||
else:
|
||||
# No custom font family, but check if we need to apply font weight
|
||||
if styles.has("font-thin") or styles.has("font-extralight") or styles.has("font-light") or styles.has("font-normal") or styles.has("font-medium") or styles.has("font-semibold") or styles.has("font-extrabold") or styles.has("font-black"):
|
||||
var default_font = FontManager.get_font("sans-serif")
|
||||
apply_font_to_label(label, default_font, styles)
|
||||
|
||||
# Apply font size
|
||||
if styles.has("font-size"):
|
||||
font_size = int(styles["font-size"])
|
||||
@@ -487,15 +497,6 @@ static func apply_styles_to_label(label: Control, styles: Dictionary, element: H
|
||||
|
||||
label.text = styled_text
|
||||
|
||||
static func apply_flex_container_properties(node: FlexContainer, styles: Dictionary) -> void:
|
||||
FlexUtils.apply_flex_container_properties(node, styles)
|
||||
|
||||
static func apply_flex_item_properties(node: Control, styles: Dictionary) -> void:
|
||||
FlexUtils.apply_flex_item_properties(node, styles)
|
||||
|
||||
static func parse_flex_value(val):
|
||||
return FlexUtils.parse_flex_value(val)
|
||||
|
||||
static func apply_body_styles(body: HTMLParser.HTMLElement, parser: HTMLParser, website_container: Control, website_background: Control) -> void:
|
||||
var styles = parser.get_element_styles_with_inheritance(body, "", [])
|
||||
|
||||
@@ -553,8 +554,35 @@ static func apply_body_styles(body: HTMLParser.HTMLElement, parser: HTMLParser,
|
||||
static func parse_radius(radius_str: String) -> int:
|
||||
return SizeUtils.parse_radius(radius_str)
|
||||
|
||||
static func apply_font_to_label(label: RichTextLabel, font_resource: Font) -> void:
|
||||
label.add_theme_font_override("normal_font", font_resource)
|
||||
static func apply_font_to_label(label: RichTextLabel, font_resource: Font, styles: Dictionary = {}) -> void:
|
||||
# Create normal font with appropriate weight
|
||||
var normal_font = SystemFont.new()
|
||||
normal_font.font_names = font_resource.font_names if font_resource is SystemFont else ["Arial"]
|
||||
|
||||
# Set weight based on styles
|
||||
var font_weight = 400 # Default normal weight
|
||||
if styles.has("font-thin"):
|
||||
font_weight = 100
|
||||
elif styles.has("font-extralight"):
|
||||
font_weight = 200
|
||||
elif styles.has("font-light"):
|
||||
font_weight = 300
|
||||
elif styles.has("font-normal"):
|
||||
font_weight = 400
|
||||
elif styles.has("font-medium"):
|
||||
font_weight = 500
|
||||
elif styles.has("font-semibold"):
|
||||
font_weight = 600
|
||||
elif styles.has("font-bold"):
|
||||
font_weight = 700
|
||||
elif styles.has("font-extrabold"):
|
||||
font_weight = 800
|
||||
elif styles.has("font-black"):
|
||||
font_weight = 900
|
||||
|
||||
normal_font.font_weight = font_weight
|
||||
|
||||
label.add_theme_font_override("normal_font", normal_font)
|
||||
|
||||
var bold_font = SystemFont.new()
|
||||
bold_font.font_names = font_resource.font_names if font_resource is SystemFont else ["Arial"]
|
||||
@@ -761,3 +789,19 @@ static func await_and_restore_transform(node: Control, target_scale: Vector2, ta
|
||||
node.scale = target_scale
|
||||
node.rotation = target_rotation
|
||||
node.pivot_offset = node.size / 2
|
||||
|
||||
static func apply_flexcontainer_centering(node: Control, styles: Dictionary) -> void:
|
||||
if not node is FlexContainer:
|
||||
return
|
||||
|
||||
var should_center_h = styles.has("mx-auto") or styles.has("justify-self-center") or (styles.has("text-align") and styles["text-align"] == "center")
|
||||
var should_center_v = styles.has("my-auto") or styles.has("align-self-center")
|
||||
|
||||
if should_center_h and not node.has_meta("size_flags_horizontal_set"):
|
||||
node.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
|
||||
if should_center_v and not node.has_meta("size_flags_vertical_set"):
|
||||
node.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
|
||||
if should_center_h or should_center_v:
|
||||
node.set_meta("size_flags_set_by_style_manager", true)
|
||||
|
||||
@@ -14,9 +14,14 @@ static func apply_flex_container_properties(node, styles: Dictionary) -> void:
|
||||
# Flex wrap
|
||||
if styles.has("flex-wrap"):
|
||||
match styles["flex-wrap"]:
|
||||
"nowrap": node.flex_wrap = FlexContainer.FlexWrap.NoWrap
|
||||
"wrap": node.flex_wrap = FlexContainer.FlexWrap.Wrap
|
||||
"wrap-reverse": node.flex_wrap = FlexContainer.FlexWrap.WrapReverse
|
||||
"nowrap":
|
||||
node.flex_wrap = FlexContainer.FlexWrap.NoWrap
|
||||
"wrap":
|
||||
node.flex_wrap = FlexContainer.FlexWrap.Wrap
|
||||
# this is probably not needed but i dont feel like testing it
|
||||
node.flex_property_changed("flex_wrap", FlexContainer.FlexWrap.Wrap)
|
||||
"wrap-reverse":
|
||||
node.flex_wrap = FlexContainer.FlexWrap.WrapReverse
|
||||
# Justify content
|
||||
if styles.has("justify-content"):
|
||||
match styles["justify-content"]:
|
||||
|
||||
@@ -195,6 +195,13 @@ static func trigger_element_restyle(element: HTMLParser.HTMLElement, dom_parser:
|
||||
var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null)
|
||||
if not dom_node:
|
||||
return
|
||||
|
||||
# Check if element has the "hidden" class before styling
|
||||
var has_hidden_class = false
|
||||
var current_style = element.get_attribute("style", "")
|
||||
if current_style.length() > 0:
|
||||
var style_classes = CSSParser.smart_split_utility_classes(current_style)
|
||||
has_hidden_class = "hidden" in style_classes
|
||||
|
||||
# margins, wrappers, etc.
|
||||
var updated_dom_node = StyleManager.apply_element_styles(dom_node, element, dom_parser)
|
||||
@@ -204,9 +211,15 @@ static func trigger_element_restyle(element: HTMLParser.HTMLElement, dom_parser:
|
||||
dom_parser.parse_result.dom_nodes[element_id] = updated_dom_node
|
||||
dom_node = updated_dom_node
|
||||
|
||||
# Apply visibility state to the correct node (wrapper or content)
|
||||
if has_hidden_class:
|
||||
dom_node.visible = false
|
||||
else:
|
||||
dom_node.visible = true
|
||||
|
||||
# Find node
|
||||
var actual_element_node = dom_node
|
||||
if dom_node is MarginContainer and dom_node.name.begins_with("MarginWrapper_"):
|
||||
if dom_node is MarginContainer and dom_node.has_meta("is_margin_wrapper"):
|
||||
if dom_node.get_child_count() > 0:
|
||||
actual_element_node = dom_node.get_child(0)
|
||||
|
||||
@@ -223,7 +236,7 @@ static func trigger_element_restyle(element: HTMLParser.HTMLElement, dom_parser:
|
||||
static func update_element_text_content(dom_node: Control, element: HTMLParser.HTMLElement, dom_parser: HTMLParser) -> void:
|
||||
# Get node
|
||||
var content_node = dom_node
|
||||
if dom_node is MarginContainer and dom_node.name.begins_with("MarginWrapper_"):
|
||||
if dom_node is MarginContainer and dom_node.has_meta("is_margin_wrapper"):
|
||||
if dom_node.get_child_count() > 0:
|
||||
content_node = dom_node.get_child(0)
|
||||
|
||||
|
||||
@@ -515,6 +515,12 @@ static func add_element_methods(vm: LuauVM, lua_api: LuaAPI) -> void:
|
||||
vm.lua_pushcallable(LuaDOMUtils._element_create_tween_wrapper, "element.createTween")
|
||||
vm.lua_setfield(-2, "createTween")
|
||||
|
||||
vm.lua_pushcallable(LuaDOMUtils._element_show_wrapper, "element.show")
|
||||
vm.lua_setfield(-2, "show")
|
||||
|
||||
vm.lua_pushcallable(LuaDOMUtils._element_hide_wrapper, "element.hide")
|
||||
vm.lua_setfield(-2, "hide")
|
||||
|
||||
_add_classlist_support(vm, lua_api)
|
||||
|
||||
vm.lua_newtable()
|
||||
@@ -881,6 +887,24 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
|
||||
# Fallback to empty array
|
||||
vm.lua_newtable()
|
||||
return 1
|
||||
"visible":
|
||||
if lua_api:
|
||||
# Get element ID and find the element
|
||||
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")
|
||||
if element:
|
||||
# Check if element has display: none (hidden class)
|
||||
var class_attr = element.get_attribute("class")
|
||||
var is_hidden = "hidden" in class_attr or element.get_attribute("style").contains("display:none") or element.get_attribute("style").contains("display: none")
|
||||
vm.lua_pushboolean(not is_hidden)
|
||||
return 1
|
||||
|
||||
# Fallback to true (visible by default)
|
||||
vm.lua_pushboolean(true)
|
||||
return 1
|
||||
_:
|
||||
# Check for DOM traversal properties first
|
||||
if lua_api:
|
||||
@@ -1034,6 +1058,48 @@ static func _element_newindex_wrapper(vm: LuauVM) -> int:
|
||||
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
"visible":
|
||||
var is_visible: bool = vm.lua_toboolean(3)
|
||||
|
||||
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")
|
||||
if element:
|
||||
var class_attr = element.get_attribute("class")
|
||||
var classes = class_attr.split(" ") if not class_attr.is_empty() else []
|
||||
|
||||
if is_visible:
|
||||
# Remove hidden class if present
|
||||
var hidden_index = classes.find("hidden")
|
||||
if hidden_index >= 0:
|
||||
classes.remove_at(hidden_index)
|
||||
var new_class_attr = " ".join(classes).strip_edges()
|
||||
element.set_attribute("class", new_class_attr)
|
||||
|
||||
# Update visual element
|
||||
var operation = {
|
||||
"type": "remove_class",
|
||||
"element_id": element_id,
|
||||
"class_name": "hidden"
|
||||
}
|
||||
emit_dom_operation(lua_api, operation)
|
||||
else:
|
||||
# Add hidden class if not present
|
||||
if not "hidden" in classes:
|
||||
classes.append("hidden")
|
||||
var new_class_attr = " ".join(classes).strip_edges()
|
||||
element.set_attribute("class", new_class_attr)
|
||||
|
||||
# Update visual element
|
||||
var operation = {
|
||||
"type": "add_class",
|
||||
"element_id": element_id,
|
||||
"class_name": "hidden"
|
||||
}
|
||||
emit_dom_operation(lua_api, operation)
|
||||
return 0
|
||||
_:
|
||||
# Store in table normally
|
||||
vm.lua_pushvalue(2)
|
||||
@@ -1041,6 +1107,71 @@ static func _element_newindex_wrapper(vm: LuauVM) -> int:
|
||||
vm.lua_rawset(1)
|
||||
return 0
|
||||
|
||||
static func _element_show_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
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")
|
||||
if element:
|
||||
var class_attr = element.get_attribute("class")
|
||||
var classes = class_attr.split(" ") if not class_attr.is_empty() else []
|
||||
|
||||
# Remove hidden class if present
|
||||
var hidden_index = classes.find("hidden")
|
||||
if hidden_index >= 0:
|
||||
classes.remove_at(hidden_index)
|
||||
var new_class_attr = " ".join(classes).strip_edges()
|
||||
element.set_attribute("class", new_class_attr)
|
||||
|
||||
# Update visual element
|
||||
var operation = {
|
||||
"type": "remove_class",
|
||||
"element_id": element_id,
|
||||
"class_name": "hidden"
|
||||
}
|
||||
emit_dom_operation(lua_api, operation)
|
||||
|
||||
return 0
|
||||
|
||||
static func _element_hide_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
return 0
|
||||
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
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")
|
||||
if element:
|
||||
var class_attr = element.get_attribute("class")
|
||||
var classes = class_attr.split(" ") if not class_attr.is_empty() else []
|
||||
|
||||
# Add hidden class if not present
|
||||
if not "hidden" in classes:
|
||||
classes.append("hidden")
|
||||
var new_class_attr = " ".join(classes).strip_edges()
|
||||
element.set_attribute("class", new_class_attr)
|
||||
|
||||
# Update visual element
|
||||
var operation = {
|
||||
"type": "add_class",
|
||||
"element_id": element_id,
|
||||
"class_name": "hidden"
|
||||
}
|
||||
emit_dom_operation(lua_api, operation)
|
||||
|
||||
return 0
|
||||
|
||||
static func _element_create_tween_wrapper(vm: LuauVM) -> int:
|
||||
var lua_api = vm.get_meta("lua_api") as LuaAPI
|
||||
if not lua_api:
|
||||
|
||||
@@ -126,6 +126,8 @@ static func _response_ok_handler(vm: LuauVM) -> int:
|
||||
return 1
|
||||
|
||||
static func make_http_request(url: String, method: String, headers: PackedStringArray, body: String) -> Dictionary:
|
||||
if url.begins_with("gurt://"):
|
||||
return make_gurt_request(url, method, headers, body)
|
||||
var http_client = HTTPClient.new()
|
||||
var response_data = {
|
||||
"status": 0,
|
||||
@@ -269,3 +271,63 @@ static func make_http_request(url: String, method: String, headers: PackedString
|
||||
|
||||
http_client.close()
|
||||
return response_data
|
||||
|
||||
static var _gurt_client: GurtProtocolClient = null
|
||||
|
||||
static func make_gurt_request(url: String, method: String, headers: PackedStringArray, body: String) -> Dictionary:
|
||||
var response_data = {
|
||||
"status": 0,
|
||||
"status_text": "Network Error",
|
||||
"headers": {},
|
||||
"body": ""
|
||||
}
|
||||
|
||||
# Reuse existing client or create new one
|
||||
if _gurt_client == null:
|
||||
_gurt_client = GurtProtocolClient.new()
|
||||
if not _gurt_client.create_client(10):
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Connection Failed"
|
||||
return response_data
|
||||
|
||||
var client = _gurt_client
|
||||
|
||||
# Convert headers array to dictionary
|
||||
var headers_dict = {}
|
||||
for header in headers:
|
||||
var parts = header.split(":", 1)
|
||||
if parts.size() == 2:
|
||||
headers_dict[parts[0].strip_edges()] = parts[1].strip_edges()
|
||||
|
||||
# Prepare request options
|
||||
var options = {
|
||||
"method": method
|
||||
}
|
||||
|
||||
if not headers_dict.is_empty():
|
||||
options["headers"] = headers_dict
|
||||
|
||||
if not body.is_empty():
|
||||
options["body"] = body
|
||||
|
||||
var response = client.request(url, options)
|
||||
|
||||
# Keep connection alive for reuse instead of disconnecting after every request
|
||||
# client.disconnect()
|
||||
|
||||
if not response:
|
||||
response_data.status = 0
|
||||
response_data.status_text = "No Response"
|
||||
return response_data
|
||||
|
||||
response_data.status = response.status_code
|
||||
response_data.status_text = response.status_message if response.status_message else "OK"
|
||||
response_data.headers = response.headers if response.headers else {}
|
||||
|
||||
var body_content = response.body if response.body else ""
|
||||
if body_content is PackedByteArray:
|
||||
response_data.body = body_content.get_string_from_utf8()
|
||||
else:
|
||||
response_data.body = str(body_content)
|
||||
|
||||
return response_data
|
||||
|
||||
@@ -55,8 +55,6 @@ func _ready():
|
||||
DisplayServer.window_set_min_size(MIN_SIZE)
|
||||
|
||||
get_viewport().size_changed.connect(_on_viewport_size_changed)
|
||||
|
||||
call_deferred("render")
|
||||
|
||||
func _on_viewport_size_changed():
|
||||
recalculate_percentage_elements(website_container)
|
||||
@@ -305,7 +303,7 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
|
||||
return null
|
||||
final_node = StyleManager.apply_element_styles(final_node, element, parser)
|
||||
# Flex item properties may still apply
|
||||
StyleManager.apply_flex_item_properties(final_node, styles)
|
||||
FlexUtils.apply_flex_item_properties(final_node, styles)
|
||||
return final_node
|
||||
|
||||
if is_flex_container:
|
||||
@@ -335,6 +333,9 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
|
||||
elif not element.text_content.is_empty():
|
||||
var new_node = await create_element_node_internal(element, parser)
|
||||
container_for_children.add_child(new_node)
|
||||
# For flex divs, we're done - no additional node creation needed
|
||||
elif element.tag_name == "div":
|
||||
pass
|
||||
else:
|
||||
final_node = await create_element_node_internal(element, parser)
|
||||
if not final_node:
|
||||
@@ -359,10 +360,10 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
|
||||
flex_container_node = first_child
|
||||
|
||||
if flex_container_node is FlexContainer:
|
||||
StyleManager.apply_flex_container_properties(flex_container_node, styles)
|
||||
FlexUtils.apply_flex_container_properties(flex_container_node, styles)
|
||||
|
||||
# Apply flex ITEM properties
|
||||
StyleManager.apply_flex_item_properties(final_node, styles)
|
||||
FlexUtils.apply_flex_item_properties(final_node, styles)
|
||||
|
||||
# Skip ul/ol and non-flex forms, they handle their own children
|
||||
var skip_general_processing = false
|
||||
@@ -473,6 +474,11 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
|
||||
"div":
|
||||
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"])
|
||||
|
||||
# For flex divs, don't create div scene - the AutoSizingFlexContainer handles it
|
||||
if is_flex_container:
|
||||
return null
|
||||
|
||||
# Create div container
|
||||
if BackgroundUtils.needs_background_wrapper(styles) or hover_styles.size() > 0:
|
||||
|
||||
Reference in New Issue
Block a user