From af8afd6e5b40809fb68a095ef5887543bbaa2ec1 Mon Sep 17 00:00:00 2001 From: Face <69168154+face-hh@users.noreply.github.com> Date: Wed, 30 Jul 2025 19:34:10 +0300 Subject: [PATCH] add class selectors --- README.md | 5 +++- Scripts/B9/CSSParser.gd | 52 ++++++++++++++++++++++++++++++++++++---- Scripts/B9/HTMLParser.gd | 20 ++++++++++++---- Scripts/Constants.gd | 2 +- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 497e305..9787971 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ gurted (verb) > “to do something smart, but also dangerous” -Sir facedev you should use the appropriate feature for this (issues) instead of using the readme, it's not professional. +wayfinder (noun) +> “a person helping others navigate” + +In traditional web, you might be familiar with the term "browser." A **wayfinder** is that, but for the **GURT** protocol. TODO: 1. Write a new **selection system**. Godot's built-in `RichTextLabel` selection is limited by the node's boundaries. In normal web, holding click and dragging your mouse across the screen will select text across multiple nodes. Godot doesn't have a "set_selected_text" property, despite having one for "get_selected_text". diff --git a/Scripts/B9/CSSParser.gd b/Scripts/B9/CSSParser.gd index dd01cf1..d09e635 100644 --- a/Scripts/B9/CSSParser.gd +++ b/Scripts/B9/CSSParser.gd @@ -21,6 +21,8 @@ class CSSRule: func calculate_specificity(): specificity = 1 + if selector.begins_with("."): + specificity += 10 # Class selectors have higher specificity than tag selectors if event_prefix.length() > 0: specificity += 10 @@ -30,13 +32,13 @@ class CSSStylesheet: func add_rule(rule: CSSRule): rules.append(rule) - func get_styles_for_element(tag_name: String, event: String = "") -> Dictionary: + func get_styles_for_element(tag_name: String, event: String = "", class_names: Array[String] = []) -> Dictionary: var styles = {} # Sort rules by specificity var applicable_rules: Array[CSSRule] = [] for rule in rules: - if selector_matches(rule, tag_name, event): + if selector_matches(rule, tag_name, event, class_names): applicable_rules.append(rule) applicable_rules.sort_custom(func(a, b): return a.specificity < b.specificity) @@ -48,9 +50,16 @@ class CSSStylesheet: return styles - func selector_matches(rule: CSSRule, tag_name: String, event: String = "") -> bool: - if rule.selector != tag_name: - return false + func selector_matches(rule: CSSRule, tag_name: String, event: String = "", cls_names: Array[String] = []) -> bool: + # Handle class selectors + if rule.selector.begins_with("."): + var cls = rule.selector.substr(1) # Remove the "." prefix + if not cls in cls_names: + return false + else: + # Handle tag selectors + if rule.selector != tag_name: + return false if rule.event_prefix.length() > 0: return rule.event_prefix == event @@ -556,6 +565,39 @@ static func parse_inline_style(style_string: String) -> Dictionary: return rule.properties +# TODO: probably a better idea to precompile the patterns, though idk how much more efficient that would be, if at all +static func is_utility_class(cls: String) -> bool: + 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)$", # font styles + "^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 + "^(hover|active):", # pseudo classes + ] + + for pattern in utility_patterns: + var regex = RegEx.new() + regex.compile(pattern) + if regex.search(cls): + return true + + return false + static func get_color(color_name: String) -> Color: # Common colors match color_name: diff --git a/Scripts/B9/HTMLParser.gd b/Scripts/B9/HTMLParser.gd index fa43005..e109ce0 100644 --- a/Scripts/B9/HTMLParser.gd +++ b/Scripts/B9/HTMLParser.gd @@ -132,8 +132,8 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = " var styles = {} - styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event)) - + var class_names = extract_class_names_from_style(element) + styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names)) # Apply inline styles (higher priority) - force override CSS rules var inline_style = element.get_attribute("style") if inline_style.length() > 0: @@ -159,7 +159,8 @@ func get_element_styles_internal(element: HTMLElement, event: String = "") -> Di # Apply CSS rules if parse_result.css_parser: - styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event)) + var class_names = extract_class_names_from_style(element) + styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names)) # Apply inline styles (higher priority) - force override CSS rules var inline_style = element.get_attribute("style") @@ -198,6 +199,17 @@ func parse_inline_style_with_event(style_string: String, event: String = "") -> return properties +func extract_class_names_from_style(element: HTMLElement) -> Array[String]: + var class_names: Array[String] = [] + var style_attr = element.get_attribute("style") + if style_attr.length() > 0: + var style_tokens = style_attr.split(" ") + for token in style_tokens: + token = token.strip_edges() + if token.length() > 0 and not CSSParser.is_utility_class(token): + class_names.append(token) + return class_names + # Creates element from CURRENT xml parser node func create_element() -> HTMLElement: var element = HTMLElement.new(xml_parser.get_node_name()) @@ -230,7 +242,7 @@ func find_all_by_class(tag: String, the_class_name: String) -> Array[HTMLElement var results: Array[HTMLElement] = [] for element in parse_result.all_elements: - if element.tag_name == tag and element.get_class() == the_class_name: + if element.tag_name == tag and element.get_class_name() == the_class_name: results.append(element) return results diff --git a/Scripts/Constants.gd b/Scripts/Constants.gd index 00628a7..6a3e95e 100644 --- a/Scripts/Constants.gd +++ b/Scripts/Constants.gd @@ -35,7 +35,7 @@ var HTML_CONTENT = """ h2 { text-[#cbd5e1] text-xl } p { text-[#94a3b8] text-base } button { bg-[#4ade80] text-[#ffffff] hover:bg-[#22c55e] active:bg-[#15803d] } - card { bg-[#1e293b] text-[#f8fafc] rounded-xl p-4 shadow-lg } + .card { bg-[#1e293b] text-[#f8fafc] rounded-xl p-4 shadow-lg }