Files
leonwww/flumi/Scripts/Utils/BaseListContainer.gd

283 lines
8.8 KiB
GDScript3
Raw Normal View History

class_name BaseListContainer
extends VBoxContainer
const BROWSER_TEXT = preload("res://Scenes/Styles/BrowserText.tres")
var list_type: String
var marker_width: float
var parser_ref: HTMLParser = null
var is_ordered: bool = false
func _ready():
child_entered_tree.connect(_on_child_added)
child_exiting_tree.connect(_on_child_removed)
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
list_type = element.get_attribute("type").to_lower()
if list_type == "":
list_type = "disc" if not is_ordered else "decimal"
parser_ref = parser
marker_width = await calculate_marker_width(element)
var index = 1
for child_element in element.children:
if child_element.tag_name == "li":
var li_node = create_li_node(child_element, index, parser)
if li_node:
add_child(li_node)
index += 1
func calculate_marker_width(element: HTMLParser.HTMLElement) -> float:
var temp_label = RichTextLabel.new()
temp_label.bbcode_enabled = true
temp_label.fit_content = true
temp_label.scroll_active = false
temp_label.theme = BROWSER_TEXT
add_child(temp_label)
var sample_text = ""
if is_ordered:
var item_count = 0
for child_element in element.children:
if child_element.tag_name == "li":
item_count += 1
sample_text = str(item_count) + "."
else:
match list_type:
"circle":
sample_text = ""
"disc":
sample_text = ""
"square":
sample_text = ""
"none":
sample_text = " "
_:
sample_text = ""
StyleManager.apply_styles_to_label(temp_label, {}, null, null, sample_text)
await get_tree().process_frame
var width = temp_label.get_content_width() + 5
remove_child(temp_label)
temp_label.queue_free()
return max(width, 20.0 if not is_ordered else 30.0)
func create_li_node(element: HTMLParser.HTMLElement, index: int, parser: HTMLParser) -> Control:
var li_container = HBoxContainer.new()
# Create marker
var marker_label = RichTextLabel.new()
marker_label.custom_minimum_size = Vector2(marker_width, 0)
marker_label.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
marker_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
marker_label.bbcode_enabled = true
marker_label.fit_content = true
marker_label.scroll_active = false
marker_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
marker_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
marker_label.theme = BROWSER_TEXT
var marker_text = get_marker_text(index)
var marker_styles = parser.get_element_styles_with_inheritance(element, "", []) if parser else {}
StyleManager.apply_styles_to_label(marker_label, marker_styles, element, parser, marker_text)
# Create content
var content_label = RichTextLabel.new()
content_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
content_label.bbcode_enabled = true
content_label.fit_content = true
content_label.scroll_active = false
content_label.theme = BROWSER_TEXT
var content_text = element.get_bbcode_formatted_text(parser)
var content_styles = parser.get_element_styles_with_inheritance(element, "", []) if parser else {}
StyleManager.apply_styles_to_label(content_label, content_styles, element, parser, content_text)
li_container.add_theme_constant_override("separation", 0)
li_container.add_child(marker_label)
li_container.add_child(content_label)
# Store element metadata on the container for renumbering
li_container.set_meta("html_element", element)
var styles = parser.get_element_styles_with_inheritance(element, "", [])
if BackgroundUtils.needs_background_wrapper(styles):
var panel_container = BackgroundUtils.create_panel_container_with_background(styles)
panel_container.name = "Li"
# Store element metadata on the panel container too
panel_container.set_meta("html_element", element)
# Get the VBoxContainer inside PanelContainer and replace it with our HBoxContainer
var vbox = panel_container.get_child(0)
panel_container.remove_child(vbox)
vbox.queue_free()
panel_container.add_child(li_container)
return panel_container
else:
return li_container
func _on_child_added(child: Node):
if child.has_meta("html_element"):
var element = child.get_meta("html_element")
if element is HTMLParser.HTMLElement and element.tag_name == "li":
call_deferred("_process_dynamic_li", child, element)
func _process_dynamic_li(child: Node, element: HTMLParser.HTMLElement):
child_entered_tree.disconnect(_on_child_added)
# Get the correct index for this new item
var current_li_count = 0
for existing_child in get_children():
if existing_child != child:
current_li_count += 1
# Remove the basic li node and replace with properly formatted one
if child.get_parent() == self:
remove_child(child)
var li_node = create_li_node(element, current_li_count + 1, parser_ref)
if li_node:
var element_id = element.get_attribute("id")
if parser_ref and element_id:
parser_ref.parse_result.dom_nodes[element_id] = li_node
add_child(li_node)
child.queue_free()
# Reconnect signal
child_entered_tree.connect(_on_child_added)
func _on_child_removed(_child: Node):
if is_ordered: # Only OL needs renumbering
call_deferred("_renumber_list")
func _renumber_list():
# Temporarily disconnect signals to avoid recursion
child_entered_tree.disconnect(_on_child_added)
child_exiting_tree.disconnect(_on_child_removed)
# Get all current li children
var li_children = []
for child in get_children():
var is_li = false
if child is HBoxContainer:
is_li = true
elif child is PanelContainer and child.get_child_count() > 0:
var inner_child = child.get_child(0)
if inner_child is HBoxContainer:
is_li = true
if is_li:
li_children.append(child)
# Renumber all existing items
for i in range(li_children.size()):
var child = li_children[i]
var marker_label = null
# Find the marker label within the child structure
if child is HBoxContainer and child.get_child_count() > 0:
marker_label = child.get_child(0)
elif child is PanelContainer and child.get_child_count() > 0:
var hbox = child.get_child(0)
if hbox is HBoxContainer and hbox.get_child_count() > 0:
marker_label = hbox.get_child(0)
# Update the marker text - recreate it completely to avoid BBCode corruption
if marker_label and marker_label is RichTextLabel:
var index = i + 1
var new_marker_text = get_marker_text(index)
# Get the HTMLElement from the li container to reapply styles properly
var element = null
if child.has_meta("html_element"):
element = child.get_meta("html_element")
elif child is PanelContainer and child.get_child_count() > 0:
var hbox = child.get_child(0)
if hbox.has_meta("html_element"):
element = hbox.get_meta("html_element")
if element and parser_ref:
var marker_styles = parser_ref.get_element_styles_with_inheritance(element, "", [])
StyleManager.apply_styles_to_label(marker_label, marker_styles, element, parser_ref, new_marker_text)
else:
# Fallback - just set the text
marker_label.text = new_marker_text
# Reconnect signals
child_entered_tree.connect(_on_child_added)
child_exiting_tree.connect(_on_child_removed)
func refresh_list():
# Force refresh of all li children for dynamically added content
var children_to_process = []
for child in get_children():
if child.has_meta("html_element"):
var element = child.get_meta("html_element")
if element is HTMLParser.HTMLElement and element.tag_name == "li":
children_to_process.append([child, element])
# Clear all children first
for child_data in children_to_process:
var child = child_data[0]
remove_child(child)
child.queue_free()
# Recalculate marker width if needed
var new_count = children_to_process.size()
if new_count > 0 and is_ordered:
marker_width = await calculate_marker_width(children_to_process[0][1])
# Re-add with correct indices
for i in range(children_to_process.size()):
var element = children_to_process[i][1]
var li_node = create_li_node(element, i + 1, parser_ref)
if li_node:
add_child(li_node)
func int_to_roman(num: int) -> String:
var values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
var symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
var result = ""
for i in range(values.size()):
while num >= values[i]:
result += symbols[i]
num -= values[i]
return result
func get_marker_text(index: int) -> String:
if is_ordered:
match list_type:
"decimal":
return str(index) + "."
"zero-lead":
return "%02d." % index
"lower-alpha":
return char(96 + index) + "."
"lower-roman":
return int_to_roman(index).to_lower() + "."
"upper-alpha":
return char(64 + index) + "."
"upper-roman":
return int_to_roman(index) + "."
"none":
return ""
_:
return str(index) + "."
else:
match list_type:
"circle":
return ""
"disc":
return ""
"square":
return ""
"none":
return " "
_:
2025-08-04 17:04:35 +03:00
return ""