move wayfinder files to Flumi folder
This commit is contained in:
171
flumi/Scripts/Utils/BackgroundUtils.gd
Normal file
171
flumi/Scripts/Utils/BackgroundUtils.gd
Normal file
@@ -0,0 +1,171 @@
|
||||
class_name BackgroundUtils
|
||||
extends RefCounted
|
||||
|
||||
static func create_stylebox_from_styles(styles: Dictionary = {}, container: Control = null) -> StyleBoxFlat:
|
||||
var style_box = StyleBoxFlat.new()
|
||||
|
||||
# Background color
|
||||
var bg_color = null
|
||||
if styles.has("background-color"):
|
||||
bg_color = styles["background-color"]
|
||||
elif container and container.has_meta("custom_css_background_color"):
|
||||
bg_color = container.get_meta("custom_css_background_color")
|
||||
|
||||
if bg_color:
|
||||
style_box.bg_color = bg_color
|
||||
else:
|
||||
style_box.bg_color = Color.TRANSPARENT
|
||||
|
||||
# Border radius
|
||||
var border_radius = null
|
||||
if styles.has("border-radius"):
|
||||
border_radius = styles["border-radius"]
|
||||
elif container and container.has_meta("custom_css_border_radius"):
|
||||
border_radius = container.get_meta("custom_css_border_radius")
|
||||
|
||||
if border_radius:
|
||||
var radius = StyleManager.parse_radius(border_radius)
|
||||
style_box.corner_radius_top_left = radius
|
||||
style_box.corner_radius_top_right = radius
|
||||
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:
|
||||
has_padding = styles.has("padding") or styles.has("padding-top") or styles.has("padding-right") or styles.has("padding-bottom") or styles.has("padding-left")
|
||||
elif container:
|
||||
has_padding = container.has_meta("padding") or container.has_meta("padding-top") or container.has_meta("padding-right") or container.has_meta("padding-bottom") or container.has_meta("padding-left")
|
||||
|
||||
if has_padding:
|
||||
# General padding
|
||||
var padding_val = null
|
||||
if styles.has("padding"):
|
||||
padding_val = StyleManager.parse_size(styles["padding"])
|
||||
elif container and container.has_meta("padding"):
|
||||
padding_val = StyleManager.parse_size(container.get_meta("padding"))
|
||||
|
||||
if padding_val:
|
||||
style_box.content_margin_left = padding_val
|
||||
style_box.content_margin_right = padding_val
|
||||
style_box.content_margin_top = padding_val
|
||||
style_box.content_margin_bottom = padding_val
|
||||
|
||||
# Individual padding values override general padding
|
||||
var padding_keys = [["padding-left", "content_margin_left"], ["padding-right", "content_margin_right"], ["padding-top", "content_margin_top"], ["padding-bottom", "content_margin_bottom"]]
|
||||
|
||||
for pair in padding_keys:
|
||||
var key = pair[0]
|
||||
var property = pair[1]
|
||||
var val = null
|
||||
|
||||
if styles.has(key):
|
||||
val = StyleManager.parse_size(styles[key])
|
||||
elif container and container.has_meta(key):
|
||||
val = StyleManager.parse_size(container.get_meta(key))
|
||||
|
||||
if val:
|
||||
style_box.set(property, val)
|
||||
|
||||
return style_box
|
||||
|
||||
# for AutoSizingFlexContainer
|
||||
static func update_background_panel(container: Control) -> void:
|
||||
var needs_background = container.has_meta("custom_css_background_color") or container.has_meta("custom_css_border_radius")
|
||||
var needs_padding = container.has_meta("padding") or container.has_meta("padding-top") or container.has_meta("padding-right") or container.has_meta("padding-bottom") or container.has_meta("padding-left")
|
||||
var background_panel = get_background_panel(container)
|
||||
|
||||
if needs_background or needs_padding:
|
||||
if not background_panel:
|
||||
background_panel = Panel.new()
|
||||
background_panel.name = "BackgroundPanel"
|
||||
background_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
background_panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
container.add_child(background_panel)
|
||||
container.move_child(background_panel, 0) # first child
|
||||
|
||||
var style_box = create_stylebox_from_styles({}, container)
|
||||
background_panel.add_theme_stylebox_override("panel", style_box)
|
||||
|
||||
elif background_panel:
|
||||
background_panel.queue_free()
|
||||
|
||||
# Helper methods for AutoSizingFlexContainer
|
||||
static func get_background_panel(container: Control) -> Panel:
|
||||
for child in container.get_children():
|
||||
if child.name == "BackgroundPanel" and child is Panel:
|
||||
return child
|
||||
return null
|
||||
|
||||
static func is_background_panel(node: Node) -> bool:
|
||||
return node.name == "BackgroundPanel" and node is Panel
|
||||
|
||||
# for any other tag
|
||||
static func create_panel_container_with_background(styles: Dictionary) -> PanelContainer:
|
||||
var panel_container = PanelContainer.new()
|
||||
panel_container.name = "Div"
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.name = "VBoxContainer"
|
||||
panel_container.add_child(vbox)
|
||||
|
||||
var style_box = create_stylebox_from_styles(styles)
|
||||
panel_container.add_theme_stylebox_override("panel", style_box)
|
||||
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") 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")
|
||||
1
flumi/Scripts/Utils/BackgroundUtils.gd.uid
Normal file
1
flumi/Scripts/Utils/BackgroundUtils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c2itd75f1j1n0
|
||||
108
flumi/Scripts/Utils/ColorUtils.gd
Normal file
108
flumi/Scripts/Utils/ColorUtils.gd
Normal file
@@ -0,0 +1,108 @@
|
||||
class_name ColorUtils
|
||||
extends RefCounted
|
||||
|
||||
static func parse_color(color_string: String) -> Color:
|
||||
color_string = color_string.strip_edges()
|
||||
|
||||
# Handle hex colors
|
||||
if color_string.begins_with("#"):
|
||||
return Color.from_string(color_string, Color.WHITE)
|
||||
|
||||
# Handle rgb/rgba
|
||||
if color_string.begins_with("rgb"):
|
||||
var regex = RegEx.new()
|
||||
regex.compile("rgba?\\(([^)]+)\\)")
|
||||
var result = regex.search(color_string)
|
||||
if result:
|
||||
var values = result.get_string(1).split(",")
|
||||
if values.size() >= 3:
|
||||
var r = values[0].strip_edges().to_float() / 255.0
|
||||
var g = values[1].strip_edges().to_float() / 255.0
|
||||
var b = values[2].strip_edges().to_float() / 255.0
|
||||
var a = 1.0
|
||||
if values.size() >= 4:
|
||||
a = values[3].strip_edges().to_float()
|
||||
return Color(r, g, b, a)
|
||||
|
||||
# Handle named colors - delegate to get_color function
|
||||
return get_color(color_string)
|
||||
|
||||
static func get_color(color_name: String) -> Color:
|
||||
# Common colors
|
||||
match color_name:
|
||||
"white": return Color.WHITE
|
||||
"black": return Color.BLACK
|
||||
"transparent": return Color.TRANSPARENT
|
||||
|
||||
# Gray scale
|
||||
"slate-50": return Color.from_string("#f8fafc", Color.WHITE)
|
||||
"slate-100": return Color.from_string("#f1f5f9", Color.WHITE)
|
||||
"slate-200": return Color.from_string("#e2e8f0", Color.WHITE)
|
||||
"slate-300": return Color.from_string("#cbd5e1", Color.WHITE)
|
||||
"slate-400": return Color.from_string("#94a3b8", Color.WHITE)
|
||||
"slate-500": return Color.from_string("#64748b", Color.WHITE)
|
||||
"slate-600": return Color.from_string("#475569", Color.WHITE)
|
||||
"slate-700": return Color.from_string("#334155", Color.WHITE)
|
||||
"slate-800": return Color.from_string("#1e293b", Color.WHITE)
|
||||
"slate-900": return Color.from_string("#0f172a", Color.WHITE)
|
||||
|
||||
"gray-50": return Color.from_string("#f9fafb", Color.WHITE)
|
||||
"gray-100": return Color.from_string("#f3f4f6", Color.WHITE)
|
||||
"gray-200": return Color.from_string("#e5e7eb", Color.WHITE)
|
||||
"gray-300": return Color.from_string("#d1d5db", Color.WHITE)
|
||||
"gray-400": return Color.from_string("#9ca3af", Color.WHITE)
|
||||
"gray-500": return Color.from_string("#6b7280", Color.WHITE)
|
||||
"gray-600": return Color.from_string("#4b5563", Color.WHITE)
|
||||
"gray-700": return Color.from_string("#374151", Color.WHITE)
|
||||
"gray-800": return Color.from_string("#1f2937", Color.WHITE)
|
||||
"gray-900": return Color.from_string("#111827", Color.WHITE)
|
||||
|
||||
# Red
|
||||
"red-50": return Color.from_string("#fef2f2", Color.WHITE)
|
||||
"red-100": return Color.from_string("#fee2e2", Color.WHITE)
|
||||
"red-200": return Color.from_string("#fecaca", Color.WHITE)
|
||||
"red-300": return Color.from_string("#fca5a5", Color.WHITE)
|
||||
"red-400": return Color.from_string("#f87171", Color.WHITE)
|
||||
"red-500": return Color.from_string("#ef4444", Color.WHITE)
|
||||
"red-600": return Color.from_string("#dc2626", Color.WHITE)
|
||||
"red-700": return Color.from_string("#b91c1c", Color.WHITE)
|
||||
"red-800": return Color.from_string("#991b1b", Color.WHITE)
|
||||
"red-900": return Color.from_string("#7f1d1d", Color.WHITE)
|
||||
|
||||
# Green
|
||||
"green-50": return Color.from_string("#f0fdf4", Color.WHITE)
|
||||
"green-100": return Color.from_string("#dcfce7", Color.WHITE)
|
||||
"green-200": return Color.from_string("#bbf7d0", Color.WHITE)
|
||||
"green-300": return Color.from_string("#86efac", Color.WHITE)
|
||||
"green-400": return Color.from_string("#4ade80", Color.WHITE)
|
||||
"green-500": return Color.from_string("#22c55e", Color.WHITE)
|
||||
"green-600": return Color.from_string("#16a34a", Color.WHITE)
|
||||
"green-700": return Color.from_string("#15803d", Color.WHITE)
|
||||
"green-800": return Color.from_string("#166534", Color.WHITE)
|
||||
"green-900": return Color.from_string("#14532d", Color.WHITE)
|
||||
|
||||
# Blue
|
||||
"blue-50": return Color.from_string("#eff6ff", Color.WHITE)
|
||||
"blue-100": return Color.from_string("#dbeafe", Color.WHITE)
|
||||
"blue-200": return Color.from_string("#bfdbfe", Color.WHITE)
|
||||
"blue-300": return Color.from_string("#93c5fd", Color.WHITE)
|
||||
"blue-400": return Color.from_string("#60a5fa", Color.WHITE)
|
||||
"blue-500": return Color.from_string("#3b82f6", Color.WHITE)
|
||||
"blue-600": return Color.from_string("#2563eb", Color.WHITE)
|
||||
"blue-700": return Color.from_string("#1d4ed8", Color.WHITE)
|
||||
"blue-800": return Color.from_string("#1e40af", Color.WHITE)
|
||||
"blue-900": return Color.from_string("#1e3a8a", Color.WHITE)
|
||||
|
||||
# Yellow
|
||||
"yellow-50": return Color.from_string("#fefce8", Color.WHITE)
|
||||
"yellow-100": return Color.from_string("#fef9c3", Color.WHITE)
|
||||
"yellow-200": return Color.from_string("#fef08a", Color.WHITE)
|
||||
"yellow-300": return Color.from_string("#fde047", Color.WHITE)
|
||||
"yellow-400": return Color.from_string("#facc15", Color.WHITE)
|
||||
"yellow-500": return Color.from_string("#eab308", Color.WHITE)
|
||||
"yellow-600": return Color.from_string("#ca8a04", Color.WHITE)
|
||||
"yellow-700": return Color.from_string("#a16207", Color.WHITE)
|
||||
"yellow-800": return Color.from_string("#854d0e", Color.WHITE)
|
||||
"yellow-900": return Color.from_string("#713f12", Color.WHITE)
|
||||
|
||||
_: return Color.BLACK
|
||||
1
flumi/Scripts/Utils/ColorUtils.gd.uid
Normal file
1
flumi/Scripts/Utils/ColorUtils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cuwui6hmwuiip
|
||||
127
flumi/Scripts/Utils/FlexUtils.gd
Normal file
127
flumi/Scripts/Utils/FlexUtils.gd
Normal file
@@ -0,0 +1,127 @@
|
||||
class_name FlexUtils
|
||||
extends RefCounted
|
||||
|
||||
static func apply_flex_container_properties(node, styles: Dictionary) -> void:
|
||||
# Flex direction - default to row if not specified
|
||||
if styles.has("flex-direction"):
|
||||
match styles["flex-direction"]:
|
||||
"row": node.flex_direction = FlexContainer.FlexDirection.Row
|
||||
"row-reverse": node.flex_direction = FlexContainer.FlexDirection.RowReverse
|
||||
"column": node.flex_direction = FlexContainer.FlexDirection.Column
|
||||
"column-reverse": node.flex_direction = FlexContainer.FlexDirection.ColumnReverse
|
||||
else:
|
||||
node.flex_direction = FlexContainer.FlexDirection.Row
|
||||
# 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
|
||||
# Justify content
|
||||
if styles.has("justify-content"):
|
||||
match styles["justify-content"]:
|
||||
"flex-start": node.justify_content = FlexContainer.JustifyContent.FlexStart
|
||||
"flex-end": node.justify_content = FlexContainer.JustifyContent.FlexEnd
|
||||
"center": node.justify_content = FlexContainer.JustifyContent.Center
|
||||
"space-between": node.justify_content = FlexContainer.JustifyContent.SpaceBetween
|
||||
"space-around": node.justify_content = FlexContainer.JustifyContent.SpaceAround
|
||||
"space-evenly": node.justify_content = FlexContainer.JustifyContent.SpaceEvenly
|
||||
# Align items
|
||||
if styles.has("align-items"):
|
||||
match styles["align-items"]:
|
||||
"flex-start": node.align_items = FlexContainer.AlignItems.FlexStart
|
||||
"flex-end": node.align_items = FlexContainer.AlignItems.FlexEnd
|
||||
"center": node.align_items = FlexContainer.AlignItems.Center
|
||||
"stretch": node.align_items = FlexContainer.AlignItems.Stretch
|
||||
"baseline": node.align_items = FlexContainer.AlignItems.Baseline
|
||||
# Align content
|
||||
if styles.has("align-content"):
|
||||
match styles["align-content"]:
|
||||
"flex-start": node.align_content = FlexContainer.AlignContent.FlexStart
|
||||
"flex-end": node.align_content = FlexContainer.AlignContent.FlexEnd
|
||||
"center": node.align_content = FlexContainer.AlignContent.Center
|
||||
"stretch": node.align_content = FlexContainer.AlignContent.Stretch
|
||||
"space-between": node.align_content = FlexContainer.AlignContent.SpaceBetween
|
||||
"space-around": node.align_content = FlexContainer.AlignContent.SpaceAround
|
||||
# Gap
|
||||
if styles.has("gap"):
|
||||
# YGGutterAll = 2
|
||||
node._root.set_gap(2, parse_flex_value(styles["gap"]))
|
||||
if styles.has("row-gap"):
|
||||
# YGGutterRow = 1
|
||||
node._root.set_gap(1, parse_flex_value(styles["row-gap"]))
|
||||
if styles.has("column-gap"):
|
||||
# YGGutterColumn = 0
|
||||
node._root.set_gap(0, parse_flex_value(styles["column-gap"]))
|
||||
|
||||
if styles.has("width"):
|
||||
var width_val = styles["width"]
|
||||
if width_val == "full":
|
||||
# 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("%"):
|
||||
node.set_meta("custom_css_width_percentage", width_val)
|
||||
else:
|
||||
node.set_meta("custom_css_width", SizingUtils.parse_size_value(width_val))
|
||||
if styles.has("height"):
|
||||
var height_val = styles["height"]
|
||||
if height_val == "full":
|
||||
# For flex containers, h-full should expand to fill parent
|
||||
node.set_meta("should_fill_vertical", true)
|
||||
elif typeof(height_val) == TYPE_STRING and height_val.ends_with("%"):
|
||||
node.set_meta("custom_css_height_percentage", height_val)
|
||||
else:
|
||||
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:
|
||||
var properties: Dictionary = node.get_meta("flex_metas", {}).duplicate(true)
|
||||
var changed = false
|
||||
|
||||
if styles.has("flex-grow"):
|
||||
properties["grow"] = float(styles["flex-grow"])
|
||||
changed = true
|
||||
if styles.has("flex-shrink"):
|
||||
properties["shrink"] = float(styles["flex-shrink"])
|
||||
changed = true
|
||||
if styles.has("flex-basis"):
|
||||
properties["basis"] = parse_flex_value(styles["flex-basis"])
|
||||
changed = true
|
||||
if styles.has("align-self"):
|
||||
var align_self_value = -1
|
||||
match styles["align-self"]:
|
||||
"auto": align_self_value = FlexContainer.AlignItems.Auto
|
||||
"flex-start": align_self_value = FlexContainer.AlignItems.FlexStart
|
||||
"flex-end": align_self_value = FlexContainer.AlignItems.FlexEnd
|
||||
"center": align_self_value = FlexContainer.AlignItems.Center
|
||||
"stretch": align_self_value = FlexContainer.AlignItems.Stretch
|
||||
"baseline": align_self_value = FlexContainer.AlignItems.Baseline
|
||||
|
||||
if align_self_value != -1:
|
||||
properties["align_self"] = align_self_value
|
||||
changed = true
|
||||
|
||||
if changed:
|
||||
node.set_meta("flex_metas", properties)
|
||||
var parent = node.get_parent()
|
||||
if parent is FlexContainer:
|
||||
parent.update_layout()
|
||||
|
||||
static func parse_flex_value(val):
|
||||
if val is float or val is int:
|
||||
return float(val)
|
||||
|
||||
if val is String:
|
||||
var s_val = val.strip_edges()
|
||||
if s_val.is_valid_float():
|
||||
return s_val.to_float()
|
||||
if s_val.ends_with("%"):
|
||||
return s_val.trim_suffix("%").to_float() / 100.0
|
||||
if s_val.ends_with("px"):
|
||||
return s_val.trim_suffix("px").to_float()
|
||||
if s_val == "auto":
|
||||
return "auto"
|
||||
|
||||
return null
|
||||
1
flumi/Scripts/Utils/FlexUtils.gd.uid
Normal file
1
flumi/Scripts/Utils/FlexUtils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dibofhomw401r
|
||||
50
flumi/Scripts/Utils/SizeUtils.gd
Normal file
50
flumi/Scripts/Utils/SizeUtils.gd
Normal file
@@ -0,0 +1,50 @@
|
||||
class_name SizeUtils
|
||||
extends RefCounted
|
||||
|
||||
# Utility functions for parsing CSS size values
|
||||
|
||||
static func parse_size(val: String) -> String:
|
||||
if val == null or val.is_empty():
|
||||
return "0px"
|
||||
|
||||
var named = {
|
||||
"0": "0px", "1": "4px", "2": "8px", "3": "12px", "4": "16px", "5": "20px", "6": "24px", "8": "32px", "10": "40px",
|
||||
"12": "48px", "16": "64px", "20": "80px", "24": "96px", "28": "112px", "32": "128px", "36": "144px", "40": "160px",
|
||||
"44": "176px", "48": "192px", "52": "208px", "56": "224px", "60": "240px", "64": "256px", "72": "288px", "80": "320px", "96": "384px",
|
||||
"3xs": "256px", "2xs": "288px", "xs": "320px", "sm": "384px", "md": "448px", "lg": "512px",
|
||||
"xl": "576px", "2xl": "672px", "3xl": "768px", "4xl": "896px", "5xl": "1024px", "6xl": "1152px", "7xl": "1280px"
|
||||
}
|
||||
if named.has(val):
|
||||
return named[val]
|
||||
# Fractional (e.g. 1/2, 1/3)
|
||||
if val.find("/") != -1:
|
||||
var parts = val.split("/")
|
||||
if parts.size() == 2 and \
|
||||
parts[1].is_valid_int() and \
|
||||
parts[0].is_valid_int() and \
|
||||
int(parts[1]) != 0:
|
||||
var frac = float(parts[0]) / float(parts[1])
|
||||
return str(frac * 100.0) + "%"
|
||||
|
||||
if val.is_valid_int():
|
||||
return str(int(val) * 16) + "px"
|
||||
return val
|
||||
|
||||
static func extract_bracket_content(string: String, start_idx: int) -> String:
|
||||
var open_idx = string.find("[", start_idx)
|
||||
if open_idx == -1:
|
||||
return ""
|
||||
var close_idx = string.find("]", open_idx)
|
||||
if close_idx == -1:
|
||||
return ""
|
||||
return string.substr(open_idx + 1, close_idx - open_idx - 1)
|
||||
|
||||
static func parse_radius(radius_str: String) -> int:
|
||||
if radius_str.ends_with("px"):
|
||||
return int(radius_str.replace("px", ""))
|
||||
elif radius_str.ends_with("rem"):
|
||||
return int(radius_str.replace("rem", "")) * 16
|
||||
elif radius_str.is_valid_float():
|
||||
return int(radius_str)
|
||||
else:
|
||||
return 0
|
||||
1
flumi/Scripts/Utils/SizeUtils.gd.uid
Normal file
1
flumi/Scripts/Utils/SizeUtils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ciqaxogsxvs24
|
||||
129
flumi/Scripts/Utils/SizingUtils.gd
Normal file
129
flumi/Scripts/Utils/SizingUtils.gd
Normal file
@@ -0,0 +1,129 @@
|
||||
class_name SizingUtils
|
||||
extends RefCounted
|
||||
|
||||
# Utility functions for handling sizes in the UI
|
||||
|
||||
const DEFAULT_VIEWPORT_WIDTH = 800.0
|
||||
const DEFAULT_VIEWPORT_HEIGHT = 600.0
|
||||
|
||||
static func parse_size_value(val):
|
||||
if val == null: return null
|
||||
if typeof(val) == TYPE_INT or typeof(val) == TYPE_FLOAT:
|
||||
return float(val)
|
||||
if val.ends_with("px"):
|
||||
return float(val.replace("px", ""))
|
||||
if val.ends_with("rem"):
|
||||
return float(val.replace("rem", "")) * 16.0
|
||||
if val.ends_with("%") or (val.ends_with("]") and "%" in val):
|
||||
var clean_val = val.replace("[", "").replace("]", "")
|
||||
return clean_val
|
||||
if val == "full":
|
||||
return null
|
||||
return float(val)
|
||||
|
||||
static func should_skip_sizing(node: Control, element, parser) -> bool:
|
||||
var element_styles = parser.get_element_styles_internal(element, "")
|
||||
|
||||
# Button sizing rules: Skip sizing only when button has no explicit size
|
||||
# AND parent doesn't have explicit width (auto-inherited sizing)
|
||||
if node is HTMLButton:
|
||||
# If button has explicit size, don't skip sizing
|
||||
if element_styles.has("width") or element_styles.has("height"):
|
||||
return false
|
||||
|
||||
# Check if width is being inherited from parent with explicit size
|
||||
var parent_element = element.parent
|
||||
if parent_element:
|
||||
var parent_styles = parser.get_element_styles_internal(parent_element, "")
|
||||
var parent_has_explicit_width = parent_styles.has("width")
|
||||
# Skip only if parent doesn't have explicit width (auto-inherited)
|
||||
return not parent_has_explicit_width
|
||||
|
||||
return true
|
||||
|
||||
# Span sizing rules: Always skip sizing for spans since they're inline elements
|
||||
# (flex containers use AutoSizingFlexContainer, not span.gd)
|
||||
elif node is HTMLSpan:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
static func apply_container_dimension_sizing(node: Control, width, height) -> void:
|
||||
if width != null:
|
||||
if is_percentage(width):
|
||||
node.set_meta("container_percentage_width", width)
|
||||
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
apply_container_percentage_sizing(node)
|
||||
else:
|
||||
node.custom_minimum_size.x = width
|
||||
node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
if height != null:
|
||||
if is_percentage(height):
|
||||
node.set_meta("container_percentage_height", height)
|
||||
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
apply_container_percentage_sizing(node)
|
||||
else:
|
||||
node.custom_minimum_size.y = height
|
||||
node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
static func apply_regular_control_sizing(node: Control, width, height) -> void:
|
||||
if width != null:
|
||||
if is_percentage(width):
|
||||
var estimated_width = calculate_percentage_size(width, DEFAULT_VIEWPORT_WIDTH)
|
||||
node.custom_minimum_size.x = estimated_width
|
||||
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
else:
|
||||
node.custom_minimum_size.x = width
|
||||
node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
if height != null:
|
||||
if is_percentage(height):
|
||||
var estimated_height = calculate_percentage_size(height, DEFAULT_VIEWPORT_HEIGHT)
|
||||
node.custom_minimum_size.y = estimated_height
|
||||
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
else:
|
||||
node.custom_minimum_size.y = height
|
||||
node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
static func is_percentage(value) -> bool:
|
||||
return typeof(value) == TYPE_STRING and value.ends_with("%")
|
||||
|
||||
static func calculate_percentage_size(percentage_str: String, fallback_size: float) -> float:
|
||||
var clean_percentage = percentage_str.replace("%", "")
|
||||
var percentage = float(clean_percentage) / 100.0
|
||||
return fallback_size * percentage
|
||||
|
||||
static func apply_container_percentage_sizing(node: Control) -> void:
|
||||
var parent = node.get_parent()
|
||||
if not parent:
|
||||
return
|
||||
|
||||
var new_min_size = node.custom_minimum_size
|
||||
|
||||
if node.has_meta("container_percentage_width"):
|
||||
var percentage_str = node.get_meta("container_percentage_width")
|
||||
var parent_width = get_parent_dimension(parent, true, DEFAULT_VIEWPORT_WIDTH)
|
||||
new_min_size.x = calculate_percentage_size(percentage_str, parent_width)
|
||||
|
||||
if node.has_meta("container_percentage_height"):
|
||||
var percentage_str = node.get_meta("container_percentage_height")
|
||||
var parent_height = get_parent_dimension(parent, false, DEFAULT_VIEWPORT_HEIGHT)
|
||||
new_min_size.y = calculate_percentage_size(percentage_str, parent_height)
|
||||
|
||||
node.custom_minimum_size = new_min_size
|
||||
|
||||
static func get_parent_dimension(parent: Control, is_width: bool, fallback: float) -> float:
|
||||
var size_value = parent.size.x if is_width else parent.size.y
|
||||
if size_value > 0:
|
||||
return size_value
|
||||
|
||||
var rect_size = parent.get_rect().size.x if is_width else parent.get_rect().size.y
|
||||
if rect_size > 0:
|
||||
return rect_size
|
||||
|
||||
var min_size = parent.custom_minimum_size.x if is_width else parent.custom_minimum_size.y
|
||||
if min_size > 0:
|
||||
return min_size
|
||||
|
||||
return fallback
|
||||
1
flumi/Scripts/Utils/SizingUtils.gd.uid
Normal file
1
flumi/Scripts/Utils/SizingUtils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bsjm8qf4ry06e
|
||||
55
flumi/Scripts/Utils/UtilityClassValidator.gd
Normal file
55
flumi/Scripts/Utils/UtilityClassValidator.gd
Normal file
@@ -0,0 +1,55 @@
|
||||
class_name UtilityClassValidator
|
||||
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 = [
|
||||
"^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl)$", # font sizes
|
||||
"^text-(left|center|right|justify)$", # text alignment
|
||||
"^text-\\[.*\\]$", # custom text colors
|
||||
"^text-(white|black|transparent|slate-\\d+|gray-\\d+|red-\\d+|green-\\d+|blue-\\d+|yellow-\\d+)$", # text colors
|
||||
"^bg-\\[.*\\]$", # custom bg colors
|
||||
"^bg-(white|black|transparent|slate-\\d+|gray-\\d+|red-\\d+|green-\\d+|blue-\\d+|yellow-\\d+)$", # bg colors
|
||||
"^(w|h|min-w|min-h|max-w|max-h)-", # sizing
|
||||
"^font-(bold|mono|italic|sans|serif)$", # font styles
|
||||
"^font-\\[.*\\]$", # custom font families with brackets
|
||||
"^font-[a-zA-Z][a-zA-Z0-9_-]*$", # custom font families without brackets
|
||||
"^underline$",
|
||||
"^flex", # flex utilities
|
||||
"^items-", # align items
|
||||
"^justify-", # justify content
|
||||
"^content-", # align content
|
||||
"^self-", # align self
|
||||
"^order-", # order
|
||||
"^gap-", # gap
|
||||
"^(p|px|py|pt|pr|pb|pl)-", # padding
|
||||
"^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:
|
||||
var regex = RegEx.new()
|
||||
regex.compile(pattern)
|
||||
compiled_patterns.append(regex)
|
||||
|
||||
static func is_utility_class(cls: String) -> bool:
|
||||
# once
|
||||
init_patterns()
|
||||
|
||||
for regex in compiled_patterns:
|
||||
if regex.search(cls):
|
||||
return true
|
||||
return false
|
||||
1
flumi/Scripts/Utils/UtilityClassValidator.gd.uid
Normal file
1
flumi/Scripts/Utils/UtilityClassValidator.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://criqj88gjrh4m
|
||||
Reference in New Issue
Block a user