From e0a8b6f414043bad0381dee4470869d46a0672e2 Mon Sep 17 00:00:00 2001
From: Face <69168154+face-hh@users.noreply.github.com>
Date: Sun, 27 Jul 2025 12:55:43 +0300
Subject: [PATCH] width, height, max-width, max-height, min-width, min-height
---
Assets/Icons/globe.svg | 1 +
Assets/Icons/globe.svg.import | 37 +++++++++
Assets/gurted.svg | 1 +
Assets/gurted.svg.import | 37 +++++++++
Scenes/Tab.tscn | 2 +-
Scripts/B9/CSSParser.gd | 91 ++++++++++++++++++++-
Scripts/MaxSizeControl.gd | 68 ++++++++++++++++
Scripts/MaxSizeControl.gd.uid | 1 +
Scripts/main.gd | 146 +++++++++++++++++++++++++++++-----
project.godot | 6 +-
10 files changed, 361 insertions(+), 29 deletions(-)
create mode 100644 Assets/Icons/globe.svg
create mode 100644 Assets/Icons/globe.svg.import
create mode 100644 Assets/gurted.svg
create mode 100644 Assets/gurted.svg.import
create mode 100644 Scripts/MaxSizeControl.gd
create mode 100644 Scripts/MaxSizeControl.gd.uid
diff --git a/Assets/Icons/globe.svg b/Assets/Icons/globe.svg
new file mode 100644
index 0000000..9ac14fb
--- /dev/null
+++ b/Assets/Icons/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Assets/Icons/globe.svg.import b/Assets/Icons/globe.svg.import
new file mode 100644
index 0000000..f2e0445
--- /dev/null
+++ b/Assets/Icons/globe.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bqpx2lgo0yecb"
+path="res://.godot/imported/globe.svg-2e20bad5dc0399b0e6d7dae13a9b962d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Icons/globe.svg"
+dest_files=["res://.godot/imported/globe.svg-2e20bad5dc0399b0e6d7dae13a9b962d.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Assets/gurted.svg b/Assets/gurted.svg
new file mode 100644
index 0000000..f7bd59c
--- /dev/null
+++ b/Assets/gurted.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Assets/gurted.svg.import b/Assets/gurted.svg.import
new file mode 100644
index 0000000..5b2fa94
--- /dev/null
+++ b/Assets/gurted.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ctpe0lbehepen"
+path="res://.godot/imported/gurted.svg-77b32dc1cb120dd6b0c05e9883f93cc4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/gurted.svg"
+dest_files=["res://.godot/imported/gurted.svg-77b32dc1cb120dd6b0c05e9883f93cc4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Scenes/Tab.tscn b/Scenes/Tab.tscn
index 7aa7bd5..e58fc08 100644
--- a/Scenes/Tab.tscn
+++ b/Scenes/Tab.tscn
@@ -6,7 +6,7 @@
[ext_resource type="Texture2D" uid="uid://c7u7a1u1v04bx" path="res://Scenes/Styles/TabGradientDefault.tres" id="3_q3baj"]
[ext_resource type="StyleBox" uid="uid://bx3sgro1ageff" path="res://Scenes/Styles/TabDefault.tres" id="4_ib6pj"]
[ext_resource type="Texture2D" uid="uid://dglkjumm1q4lo" path="res://Assets/Icons/23x23-empty.svg" id="5_ib6pj"]
-[ext_resource type="Texture2D" uid="uid://bslojb4cmwnvn" path="res://icon.svg" id="6_ib6pj"]
+[ext_resource type="Texture2D" uid="uid://bqpx2lgo0yecb" path="res://Assets/Icons/globe.svg" id="6_ib6pj"]
[ext_resource type="StyleBox" uid="uid://dn8exdnk8tjce" path="res://Scenes/Styles/CloseButtonHover.tres" id="6_pisds"]
[ext_resource type="StyleBox" uid="uid://dn6r16retee3l" path="res://Scenes/Styles/CloseButtonNormal.tres" id="7_1ohlo"]
diff --git a/Scripts/B9/CSSParser.gd b/Scripts/B9/CSSParser.gd
index f3bb459..91adb51 100644
--- a/Scripts/B9/CSSParser.gd
+++ b/Scripts/B9/CSSParser.gd
@@ -162,6 +162,32 @@ func parse_utility_class(rule: CSSRule, utility_name: String) -> void:
rule.properties["background-color"] = color
return
+ # e.g. max-w-[123px], w-[50%], h-[2rem]
+ if utility_name.match("^max-w-\\[.*\\]$"):
+ var val = extract_bracket_content(utility_name, 6)
+ rule.properties["max-width"] = val
+ return
+ if utility_name.match("^max-h-\\[.*\\]$"):
+ var val = extract_bracket_content(utility_name, 6)
+ rule.properties["max-height"] = val
+ return
+ if utility_name.match("^min-w-\\[.*\\]$"):
+ var val = extract_bracket_content(utility_name, 6)
+ rule.properties["min-width"] = val
+ return
+ if utility_name.match("^min-h-\\[.*\\]$"):
+ var val = extract_bracket_content(utility_name, 6)
+ rule.properties["min-height"] = val
+ return
+ if utility_name.match("^w-\\[.*\\]$"):
+ var val = extract_bracket_content(utility_name, 2)
+ rule.properties["width"] = val
+ return
+ if utility_name.match("^h-\\[.*\\]$"):
+ var val = extract_bracket_content(utility_name, 2)
+ rule.properties["height"] = val
+ return
+
# Handle font weight
if utility_name == "font-bold":
rule.properties["font-bold"] = true
@@ -194,19 +220,76 @@ func parse_utility_class(rule: CSSRule, utility_name: String) -> void:
"text-4xl": rule.properties["font-size"] = 36
"text-5xl": rule.properties["font-size"] = 48
"text-6xl": rule.properties["font-size"] = 60
+
+ # Handle text alignment classes
+ "text-left": rule.properties["text-align"] = "left"
+ "text-center": rule.properties["text-align"] = "center"
+ "text-right": rule.properties["text-align"] = "right"
+ "text-justify": rule.properties["text-align"] = "justify"
+ # Width
+ if utility_name.begins_with("w-"):
+ var val = utility_name.substr(2)
+ rule.properties["width"] = parse_size(val)
+ return
+ # Height
+ if utility_name.begins_with("h-"):
+ var val = utility_name.substr(2)
+ rule.properties["height"] = parse_size(val)
+ return
+ # Min width
+ if utility_name.begins_with("min-w-"):
+ var val = utility_name.substr(6)
+ rule.properties["min-width"] = parse_size(val)
+ return
+ # Min height
+ if utility_name.begins_with("min-h-"):
+ var val = utility_name.substr(6)
+ rule.properties["min-height"] = parse_size(val)
+ return
+ # Max width
+ if utility_name.begins_with("max-w-"):
+ var val = utility_name.substr(6)
+ rule.properties["max-width"] = parse_size(val)
+ return
+ # Max height
+ if utility_name.begins_with("max-h-"):
+ var val = utility_name.substr(6)
+ rule.properties["max-height"] = parse_size(val)
+ return
+
# Handle more utility classes as needed
# Add more cases here for other utilities
+func parse_size(val: String) -> String:
+ 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():
+ 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
+
# Helper to extract content inside first matching brackets after a given index
-func extract_bracket_content(str: String, start_idx: int) -> String:
- var open_idx = str.find("[", start_idx)
+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 = str.find("]", open_idx)
+ var close_idx = string.find("]", open_idx)
if close_idx == -1:
return ""
- return str.substr(open_idx + 1, close_idx - open_idx - 1)
+ return string.substr(open_idx + 1, close_idx - open_idx - 1)
func parse_color(color_string: String) -> Color:
print("DEBUG: parsing color: ", color_string)
diff --git a/Scripts/MaxSizeControl.gd b/Scripts/MaxSizeControl.gd
new file mode 100644
index 0000000..a12d352
--- /dev/null
+++ b/Scripts/MaxSizeControl.gd
@@ -0,0 +1,68 @@
+@tool
+class_name MaxSizeControl
+extends Control
+
+@export var max_size: Vector2 = Vector2(-1, -1):
+ set(value):
+ max_size = value
+ _enforce_size_limits()
+
+var content_node: Control
+
+func _ready():
+ # Auto-detect content node
+ if get_child_count() > 0:
+ setup_content_node(get_child(0))
+
+ # Connect to our own resize
+ resized.connect(_on_resized)
+
+func setup_content_node(node: Control):
+ content_node = node
+ if content_node:
+ # Make content fill the container initially
+ content_node.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
+
+ # Connect to content changes
+ if not content_node.minimum_size_changed.is_connected(_on_content_changed):
+ content_node.minimum_size_changed.connect(_on_content_changed)
+
+ _enforce_size_limits()
+
+func _on_content_changed():
+ _enforce_size_limits()
+
+func _on_resized():
+ _enforce_size_limits()
+
+func _enforce_size_limits():
+ if not content_node:
+ return
+
+ var target_width = max_size.x if max_size.x > 0 else content_node.get_combined_minimum_size().x
+ var target_height = max_size.y if max_size.y > 0 else content_node.get_combined_minimum_size().y
+
+ custom_minimum_size = Vector2(target_width, target_height)
+
+ # Set children's minimum size to match the constrained size
+ for child in get_children():
+ if child is Control:
+ child.custom_minimum_size = Vector2(target_width, target_height)
+
+ # Force the content to fit within our bounds and enable clipping
+ content_node.size = Vector2(target_width, target_height)
+ content_node.position = Vector2.ZERO
+
+ # Always enable clipping if max_size is set
+ var needs_clipping = max_size.x > 0 or max_size.y > 0
+ content_node.clip_contents = needs_clipping
+ clip_contents = true
+
+func _get_minimum_size() -> Vector2:
+ # Only use max_size, ignore content's natural size
+ var final_size = Vector2.ZERO
+ if max_size.x > 0:
+ final_size.x = max_size.x
+ if max_size.y > 0:
+ final_size.y = max_size.y
+ return final_size
diff --git a/Scripts/MaxSizeControl.gd.uid b/Scripts/MaxSizeControl.gd.uid
new file mode 100644
index 0000000..563d1e3
--- /dev/null
+++ b/Scripts/MaxSizeControl.gd.uid
@@ -0,0 +1 @@
+uid://cmxmcn3ghw8t2
diff --git a/Scripts/main.gd b/Scripts/main.gd
index 4f19610..c8ec4ef 100644
--- a/Scripts/main.gd
+++ b/Scripts/main.gd
@@ -82,7 +82,11 @@ both spaces and
line breaks
-