From 05a959647de4455aba4f310b2c7469472167f2ce Mon Sep 17 00:00:00 2001 From: Face <69168154+face-hh@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:59:43 +0300 Subject: [PATCH] CSS selectors in select() Lua API // fix class attr functions --- docs/docusaurus.config.ts | 1 + flumi/Scripts/B9/CSSParser.gd | 48 +++++++++++++----------- flumi/Scripts/B9/HTMLParser.gd | 23 ++++-------- flumi/Scripts/B9/Lua.gd | 34 +++-------------- flumi/Scripts/Constants.gd | 8 +++- flumi/Scripts/Utils/SelectorUtils.gd | 30 +++++++++++++++ flumi/Scripts/Utils/SelectorUtils.gd.uid | 1 + 7 files changed, 79 insertions(+), 66 deletions(-) create mode 100644 flumi/Scripts/Utils/SelectorUtils.gd create mode 100644 flumi/Scripts/Utils/SelectorUtils.gd.uid diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 0948428..c2fe598 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -137,6 +137,7 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + additionalLanguages: ['lua'], }, } satisfies Preset.ThemeConfig, }; diff --git a/flumi/Scripts/B9/CSSParser.gd b/flumi/Scripts/B9/CSSParser.gd index 4c745ac..93f699a 100644 --- a/flumi/Scripts/B9/CSSParser.gd +++ b/flumi/Scripts/B9/CSSParser.gd @@ -112,7 +112,7 @@ class CSSStylesheet: match rule.selector_type: "simple": - return matches_simple_selector(rule.selector_parts[0], tag_name, cls_names) + return matches_simple_selector_with_element(rule.selector_parts[0], element) "descendant": return matches_descendant_selector(rule.selector_parts, element) "child": @@ -130,16 +130,33 @@ class CSSStylesheet: if selector.begins_with("."): var cls = selector.substr(1) return cls in cls_names + elif selector.begins_with("#"): + # need access to the element to check its ID + return false # will be handled by matches_simple_selector_with_element else: return selector == tag_name + func matches_simple_selector_with_element(selector: String, element: HTMLParser.HTMLElement) -> bool: + if not element: + return false + + if selector.begins_with("."): + var cls = selector.substr(1) + var cls_names = HTMLParser.extract_class_names(element) + return cls in cls_names + elif selector.begins_with("#"): + var element_id = selector.substr(1) + return element.get_attribute("id") == element_id + else: + return selector == element.tag_name + func matches_descendant_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool: if not element or parts.size() < 2: return false # Last part should match current element var last_part = parts[-1].strip_edges() - if not matches_simple_selector(last_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(last_part, element): return false # Check ancestors for remaining parts @@ -148,7 +165,7 @@ class CSSStylesheet: while current_element and part_index >= 0: var part = parts[part_index].strip_edges() - if matches_simple_selector(part, current_element.tag_name, get_element_class_names(current_element)): + if matches_simple_selector_with_element(part, current_element): part_index -= 1 if part_index < 0: return true @@ -164,11 +181,11 @@ class CSSStylesheet: var parent_part = parts[0].strip_edges() # Element must match the child part - if not matches_simple_selector(child_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(child_part, element): return false # Parent must match the parent part - return matches_simple_selector(parent_part, element.parent.tag_name, get_element_class_names(element.parent)) + return matches_simple_selector_with_element(parent_part, element.parent) func matches_adjacent_sibling_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool: if not element or not element.parent or parts.size() != 2: @@ -177,7 +194,7 @@ class CSSStylesheet: var second_part = parts[1].strip_edges() var first_part = parts[0].strip_edges() - if not matches_simple_selector(second_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(second_part, element): return false # Find previous sibling @@ -187,7 +204,7 @@ class CSSStylesheet: return false var prev_sibling = siblings[element_index - 1] - return matches_simple_selector(first_part, prev_sibling.tag_name, get_element_class_names(prev_sibling)) + return matches_simple_selector_with_element(first_part, prev_sibling) func matches_general_sibling_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool: if not element or not element.parent or parts.size() != 2: @@ -196,7 +213,7 @@ class CSSStylesheet: var second_part = parts[1].strip_edges() var first_part = parts[0].strip_edges() - if not matches_simple_selector(second_part, element.tag_name, get_element_class_names(element)): + if not matches_simple_selector_with_element(second_part, element): return false # Check all previous siblings @@ -205,7 +222,7 @@ class CSSStylesheet: for i in range(element_index): var sibling = siblings[i] - if matches_simple_selector(first_part, sibling.tag_name, get_element_class_names(sibling)): + if matches_simple_selector_with_element(first_part, sibling): return true return false @@ -218,7 +235,7 @@ class CSSStylesheet: var attribute_part = parts[1].strip_edges() # Check if element matches - if element_part != "" and not matches_simple_selector(element_part, tag_name, cls_names): + if element_part != "" and not matches_simple_selector_with_element(element_part, element): return false # Parse attribute condition @@ -261,17 +278,6 @@ class CSSStylesheet: attr_value = attr_value.substr(1, attr_value.length() - 2) return {"name": attr_name, "value": attr_value} - - func get_element_class_names(element: HTMLParser.HTMLElement) -> Array[String]: - var class_names: Array[String] = [] - var class_attr = element.get_attribute("class") - if class_attr.length() > 0: - var classes = class_attr.split(" ") - for cls in classes: - cls = cls.strip_edges() - if cls.length() > 0: - class_names.append(cls) - return class_names var stylesheet: CSSStylesheet var css_text: String diff --git a/flumi/Scripts/B9/HTMLParser.gd b/flumi/Scripts/B9/HTMLParser.gd index f745aea..59d8274 100644 --- a/flumi/Scripts/B9/HTMLParser.gd +++ b/flumi/Scripts/B9/HTMLParser.gd @@ -142,7 +142,7 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = " var styles = {} - var class_names = get_css_class_names(element) + var class_names = extract_class_names(element) styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element)) # Apply inline styles (higher priority) - force override CSS rules var inline_style = element.get_attribute("style") @@ -169,7 +169,7 @@ func get_element_styles_internal(element: HTMLElement, event: String = "") -> Di # Apply CSS rules if parse_result.css_parser: - var class_names = get_css_class_names(element) + var class_names = extract_class_names(element) styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element)) # Apply inline styles (higher priority) - force override CSS rules @@ -227,18 +227,7 @@ func parse_inline_style_with_event(style_string: String, event: String = "") -> return properties -func get_css_class_names(element: HTMLElement) -> Array[String]: - var class_names: Array[String] = [] - var class_attr = element.get_attribute("class") - if class_attr.length() > 0: - var classes = class_attr.split(" ") - for cls in classes: - cls = cls.strip_edges() - if cls.length() > 0: - class_names.append(cls) - return class_names - -func extract_class_names_from_style(element: HTMLElement) -> Array[String]: +static func extract_class_names(element: HTMLElement) -> Array[String]: var class_names: Array[String] = [] var style_attr = element.get_attribute("style") if style_attr.length() > 0: @@ -281,8 +270,10 @@ 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_name() == the_class_name: - results.append(element) + if element.tag_name == tag: + var class_names = extract_class_names(element) + if the_class_name in class_names: + results.append(element) return results diff --git a/flumi/Scripts/B9/Lua.gd b/flumi/Scripts/B9/Lua.gd index 0b9e210..66803c7 100644 --- a/flumi/Scripts/B9/Lua.gd +++ b/flumi/Scripts/B9/Lua.gd @@ -43,21 +43,18 @@ func get_or_assign_element_id(element: HTMLParser.HTMLElement) -> String: func _gurt_select_handler(vm: LuauVM) -> int: var selector: String = vm.luaL_checkstring(1) - var element_id = "" - if selector.begins_with("#"): - element_id = selector.substr(1) - else: + var element = SelectorUtils.find_first_matching(selector, dom_parser.parse_result.all_elements) + if not element: vm.lua_pushnil() return 1 - var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null) - if not dom_node: - vm.lua_pushnil() - return 1 + var element_id = get_or_assign_element_id(element) vm.lua_newtable() vm.lua_pushstring(element_id) vm.lua_setfield(-2, "_element_id") + vm.lua_pushstring(element.tag_name) + vm.lua_setfield(-2, "_tag_name") add_element_methods(vm) return 1 @@ -66,26 +63,7 @@ func _gurt_select_handler(vm: LuauVM) -> int: func _gurt_select_all_handler(vm: LuauVM) -> int: var selector: String = vm.luaL_checkstring(1) - var elements: Array[HTMLParser.HTMLElement] = [] - - # Handle different selector types - if selector.begins_with("#"): - # ID selector - find single element - var element_id = selector.substr(1) - var element = dom_parser.find_by_id(element_id) - if element: - elements.append(element) - LuaPrintUtils.lua_print_direct("WARNING: Using ID selector in select_all is not recommended, use select instead.") - elif selector.begins_with("."): - # Class selector - find all elements with class - var cls = selector.substr(1) - for element in dom_parser.parse_result.all_elements: - var element_classes = CSSParser.smart_split_utility_classes(element.get_attribute("style")) - if cls in element_classes: - elements.append(element) - else: - # Tag selector - find all elements with tag name - elements = dom_parser.find_all(selector) + var elements = SelectorUtils.find_all_matching(selector, dom_parser.parse_result.all_elements) vm.lua_newtable() var index = 1 diff --git a/flumi/Scripts/Constants.gd b/flumi/Scripts/Constants.gd index ebecbf7..52d1798 100644 --- a/flumi/Scripts/Constants.gd +++ b/flumi/Scripts/Constants.gd @@ -823,7 +823,11 @@ var HTML_CONTENT = """
local mySignal = Signal.new() local dataSignal = Signal.new() local userActionSignal = Signal.new() - + print(".container > div: ", gurt.selectAll('.container > div')) + print(".container div: ", gurt.selectAll('.container div')) + print("button[disabled]: ", gurt.selectAll('button[disabled]')) + print(".container: ", gurt.selectAll('.container')) + print("#log-area: ", gurt.selectAll('#log-area')) -- Get UI elements local logArea = gurt.select('#log-area') local statusDisplay = gurt.select('#status-display') @@ -984,6 +988,8 @@ var HTML_CONTENT = """