border styles

This commit is contained in:
Face
2025-08-01 21:16:48 +03:00
parent 777f24ccb4
commit aa4ee1b93c
7 changed files with 223 additions and 15 deletions

View File

@@ -18,6 +18,7 @@ TODO:
9. Installer should register **gurt://** as a valid protocol thru the registry.
10. < input type=**datetime** />, essentially a type "date" but with a vertical separator, then `mm | ss | FORMAT` layout for time.
11. **< table >** component. [🔗 Related Godot proposal](https://github.com/godotengine/godot-proposals/issues/97)
12. **< canvas >** component should be theoretically impossible by exposing Godot `_draw()` APIs to Lua.
Issues:
1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing

View File

@@ -508,6 +508,86 @@ static func parse_utility_class_internal(rule: CSSRule, utility_name: String) ->
rule.properties["my-auto"] = true
return
# Apply border properties
var apply_border = func(side: String, width: String = "", color = null, style: String = "solid"):
var prefix = "border" + ("-" + side if side != "" else "")
if width != "":
rule.properties[prefix + "-width"] = width
if color != null:
rule.properties[prefix + "-color"] = color
if style != "":
rule.properties[prefix + "-style"] = style
# Handle border utilities
if utility_name == "border":
apply_border.call("", "1px", Color.BLACK)
return
if utility_name == "border-none":
rule.properties["border-style"] = "none"
return
# Individual border sides - pattern: border-{side}-{value}
var border_sides = ["t", "r", "b", "l"]
var side_map = {"t": "top", "r": "right", "b": "bottom", "l": "left"}
for side in border_sides:
var short_side = side
var full_side = side_map[side]
# Basic side border (e.g., border-t)
if utility_name == "border-" + short_side:
apply_border.call(full_side, "1px")
return
# Side with value (e.g., border-t-2, border-t-red-500)
if utility_name.begins_with("border-" + short_side + "-"):
var val = utility_name.substr(9) # after "border-X-"
# Check for bracket notation first
if utility_name.begins_with("border-" + short_side + "-[") and utility_name.ends_with("]"):
var value = SizeUtils.extract_bracket_content(utility_name, 9)
if value.begins_with("#") or ColorUtils.parse_color(value) != null:
apply_border.call(full_side, "", ColorUtils.parse_color(value))
else:
apply_border.call(full_side, value)
return
# Check if it's a numeric width
if val.is_valid_int():
apply_border.call(full_side, str(int(val)) + "px")
return
# Check if it's a color
var color = ColorUtils.get_color(val)
if color != null:
apply_border.call(full_side, "", color)
return
# General border width (e.g., border-2)
if utility_name.begins_with("border-"):
var val = utility_name.substr(7)
# Custom border width like border-[2px]
if utility_name.begins_with("border-[") and utility_name.ends_with("]"):
var value = SizeUtils.extract_bracket_content(utility_name, 7)
if value.begins_with("#"):
apply_border.call("", "", ColorUtils.parse_color(value))
else:
apply_border.call("", value)
return
# Numeric width
if val.is_valid_int():
apply_border.call("", str(int(val)) + "px")
return
# Color name
var color = ColorUtils.get_color(val)
if color != null:
apply_border.call("", "", color)
return
# Handle more utility classes as needed
# Add more cases here for other utilities

View File

@@ -24,7 +24,7 @@ pre { text-xl font-mono }
button { bg-[#1b1b1b] rounded-md text-white hover:bg-[#2a2a2a] active:bg-[#101010] }
"""
var HTML_CONTENT = """<head>
var HTML_CONTENT2 = """<head>
<title>My Custom Dashboard</title>
<icon src="https://cdn-icons-png.flaticon.com/512/1828/1828774.png">
<meta name="theme-color" content="#1a202c">
@@ -99,7 +99,7 @@ var HTML_CONTENT = """<head>
</body>
""".to_utf8_buffer()
var HTML_CONTENT2 = """<head>
var HTML_CONTENT = """<head>
<title>My cool web</title>
<icon src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Google_%22G%22_logo.svg/768px-Google_%22G%22_logo.svg.png\">
@@ -141,7 +141,7 @@ var HTML_CONTENT2 = """<head>
<code>this is code<span> THIS IS A SPAN AND SHOULDNT BE ANY DIFFERENT</span></code>
<p>
<a href=\"https://youtube.com\">Hello gang</a>
<a href="https://youtube.com">Hello gang</a>
</p>
<pre>
@@ -152,9 +152,36 @@ both spaces and
line breaks
</pre>
<p style=\"text-center w-32 h-32\">
So
</p>
<p style="text-center w-32 h-32">
So
</p>
<!-- Border examples -->
<div style="border p-2 mb-2">border</div>
<div style="border-2 p-2 mb-2">border-2</div>
<div style="border-4 p-2 mb-2">border-4</div>
<div style="border-2 border-red-500 p-2 mb-2">border-2 border-red-500</div>
<div style="border p-2 mb-2">border-solid</div>
<div style="border border-dashed p-2 mb-2">border-dashed</div>
<div style="border border-dotted p-2 mb-2">border-dotted</div>
<div style="border-none p-2 mb-2">border-none</div>
<div style="border-t p-2 mb-2">border-t</div>
<div style="border-r p-2 mb-2">border-r</div>
<div style="border-b p-2 mb-2">border-b</div>
<div style="border-l p-2 mb-2">border-l</div>
<div style="border-t-4 p-2 mb-2">border-t-4</div>
<div style="border-b-2 p-2 mb-2">border-b-2</div>
<div style="border-l-6 p-2 mb-2">border-l-6</div>
<div style="border-t-3 border-green-500 p-2 mb-2">border-t-3 border-green-500</div>
<div style="border border-white p-2 mb-2">border-white</div>
<div style="border border-black p-2 mb-2">border-black</div>
<div style="border border-transparent p-2 mb-2">border-transparent</div>
<div style="border border-gray-400 p-2 mb-2">border-gray-400</div>
<div style="border border-slate-700 p-2 mb-2">border-slate-700</div>
<div style="border border-red-500 p-2 mb-2">border-red-500</div>
<div style="border border-green-600 p-2 mb-2">border-green-600</div>
<div style="border border-blue-400 p-2 mb-2">border-blue-400</div>
<div style="border border-yellow-300 p-2 mb-2">border-yellow-300</div>
<select style=\"text-center max-w-5 max-h-32\">
<option value=\"test1\">Test 1</option>

View File

@@ -84,14 +84,30 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
if label and label != node:
label.anchors_preset = Control.PRESET_FULL_RECT
# Apply background color and border radius
if styles.has("background-color") or styles.has("border-radius"):
# Apply background color, border radius, borders
var needs_styling = styles.has("background-color") or styles.has("border-radius") or styles.has("border-width") or styles.has("border-top-width") or styles.has("border-right-width") or styles.has("border-bottom-width") or styles.has("border-left-width") or styles.has("border-color")
if needs_styling:
var target_node_for_bg = node if node is FlexContainer else label
if target_node_for_bg:
if styles.has("background-color"):
target_node_for_bg.set_meta("custom_css_background_color", styles["background-color"])
if styles.has("border-radius"):
target_node_for_bg.set_meta("custom_css_border_radius", styles["border-radius"])
# Border properties
if styles.has("border-width"):
target_node_for_bg.set_meta("custom_css_border_width", styles["border-width"])
if styles.has("border-color"):
target_node_for_bg.set_meta("custom_css_border_color", styles["border-color"])
# Individual border sides
var border_sides = ["top", "right", "bottom", "left"]
for side in border_sides:
var width_key = "border-" + side + "-width"
if styles.has(width_key):
target_node_for_bg.set_meta("custom_css_" + width_key.replace("-", "_"), styles[width_key])
if target_node_for_bg.has_method("add_background_rect"):
target_node_for_bg.call_deferred("add_background_rect")

View File

@@ -30,6 +30,60 @@ static func create_stylebox_from_styles(styles: Dictionary = {}, container: Cont
style_box.corner_radius_bottom_left = radius
style_box.corner_radius_bottom_right = radius
# Border properties
var has_border = false
style_box.border_width_top = 0
style_box.border_width_right = 0
style_box.border_width_bottom = 0
style_box.border_width_left = 0
var general_border_width = null
if styles.has("border-width"):
general_border_width = styles["border-width"]
elif container and container.has_meta("custom_css_border_width"):
general_border_width = container.get_meta("custom_css_border_width")
if general_border_width:
has_border = true
var parsed_width = StyleManager.parse_size(general_border_width)
style_box.border_width_top = parsed_width
style_box.border_width_right = parsed_width
style_box.border_width_bottom = parsed_width
style_box.border_width_left = parsed_width
var individual_border_keys = [
["border-top-width", "border_width_top"],
["border-right-width", "border_width_right"],
["border-bottom-width", "border_width_bottom"],
["border-left-width", "border_width_left"]
]
for pair in individual_border_keys:
var style_key = pair[0]
var property_name = pair[1]
var width = null
var meta_key = "custom_css_" + style_key.replace("-", "_")
if styles.has(style_key):
width = styles[style_key]
elif container and container.has_meta(meta_key):
width = container.get_meta(meta_key)
if width:
has_border = true
var parsed_width = StyleManager.parse_size(width)
style_box.set(property_name, parsed_width)
var border_color = Color.BLACK
if styles.has("border-color"):
border_color = styles["border-color"]
elif container and container.has_meta("custom_css_border_color"):
border_color = container.get_meta("custom_css_border_color")
if has_border:
style_box.border_color = border_color
# Padding as content margins
var has_padding = false
if styles.size() > 0:
@@ -114,4 +168,4 @@ static func create_panel_container_with_background(styles: Dictionary) -> PanelC
return panel_container
static func needs_background_wrapper(styles: Dictionary) -> bool:
return styles.has("background-color") or styles.has("border-radius") or styles.has("padding") or styles.has("padding-top") or styles.has("padding-right") or styles.has("padding-bottom") or styles.has("padding-left")
return styles.has("background-color") or styles.has("border-radius") or styles.has("padding") or styles.has("padding-top") or styles.has("padding-right") or styles.has("padding-bottom") or styles.has("padding-left") or styles.has("border-width") or styles.has("border-top-width") or styles.has("border-right-width") or styles.has("border-bottom-width") or styles.has("border-left-width") or styles.has("border-color") or styles.has("border-style") or styles.has("border-top-color") or styles.has("border-right-color") or styles.has("border-bottom-color") or styles.has("border-left-color")

View File

@@ -3,6 +3,7 @@ extends RefCounted
static var compiled_patterns: Array = []
# TODO: hardcoded colors gotta be swapped with Tailwind colors. stuff like "text-red-500" is considered a selector class
static func init_patterns():
if compiled_patterns.size() == 0:
var utility_patterns = [
@@ -28,6 +29,15 @@ static func init_patterns():
"^rounded", # border radius
"^basis-", # flex basis
"^(mx|my|m)-auto$", # margin auto for centering
"^border$", # general border
"^border-\\d+$", # border width (e.g., border-2)
"^border-\\[.*\\]$", # custom border width/color (e.g., border-[2px], border-[#ff0000])
"^border-none$", # border styles
"^border-(t|r|b|l)$", # individual border sides (e.g., border-t)
"^border-(t|r|b|l)-\\d+$", # individual border side widths (e.g., border-t-2)
"^border-(t|r|b|l)-\\[.*\\]$", # custom individual border sides (e.g., border-t-[2px])
"^border-(t|r|b|l)-(white|black|transparent|slate-\\d+|gray-\\d+|red-\\d+|green-\\d+|blue-\\d+|yellow-\\d+)$", # individual border side colors
"^border-(white|black|transparent|slate-\\d+|gray-\\d+|red-\\d+|green-\\d+|blue-\\d+|yellow-\\d+)$", # border colors
"^(hover|active):", # pseudo classes
]
for pattern in utility_patterns:

View File

@@ -132,6 +132,13 @@ func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
return false
func is_text_only_element(element: HTMLParser.HTMLElement) -> bool:
if element.children.size() == 0:
var text = element.get_collapsed_text()
return not text.is_empty()
return false
func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) -> Control:
var styles = parser.get_element_styles_with_inheritance(element, "", [])
var is_flex_container = styles.has("display") and ("flex" in styles["display"])
@@ -269,7 +276,7 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
node.init(element, parser)
"span", "b", "i", "u", "small", "mark", "code", "a":
node = SPAN.instantiate()
node.init(element)
node.init(element, parser)
"ul":
node = UL.instantiate()
website_container.add_child(node)
@@ -282,24 +289,37 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
return node
"li":
node = LI.instantiate()
node.init(element)
node.init(element, parser)
"select":
node = SELECT.instantiate()
node.init(element)
"option":
node = OPTION.instantiate()
node.init(element)
node.init(element, parser)
"textarea":
node = TEXTAREA.instantiate()
node.init(element)
"div":
var styles = parser.get_element_styles_with_inheritance(element, "", [])
# Create div container
if BackgroundUtils.needs_background_wrapper(styles):
node = BackgroundUtils.create_panel_container_with_background(styles)
else:
node = VBoxContainer.new()
node.name = "Div"
node = DIV.instantiate()
node.init(element)
var has_only_text = is_text_only_element(element)
if has_only_text:
var p_node = P.instantiate()
p_node.init(element)
var container_for_children = node
if node is PanelContainer and node.get_child_count() > 0:
container_for_children = node.get_child(0) # The VBoxContainer inside
safe_add_child(container_for_children, p_node)
_:
return null