From e19d4b7f61b9a63f3dbdfc600e62d69b27cbf22d Mon Sep 17 00:00:00 2001
From: Face <69168154+face-hh@users.noreply.github.com>
Date: Sat, 23 Aug 2025 14:45:45 +0300
Subject: [PATCH] use hbox instead of richtextlabel for nested text tags,
complete invite creation
---
README.md | 1 -
dns/frontend/index.html | 2 +-
dns/frontend/register.html | 2 +-
dns/frontend/register.lua | 15 ++-
dns/frontend/signup.html | 2 +-
dns/src/gurt_server/auth_routes.rs | 73 ++++++++------
dns/src/gurt_server/routes.rs | 31 +++---
flumi/Scenes/Tags/p.tscn | 15 +--
flumi/Scripts/B9/HTMLParser.gd | 8 +-
flumi/Scripts/B9/Lua.gd | 30 ++++--
flumi/Scripts/Tags/input.gd | 10 +-
flumi/Scripts/Tags/p.gd | 150 +++++++++++++++++++----------
12 files changed, 205 insertions(+), 134 deletions(-)
diff --git a/README.md b/README.md
index eb9a709..fcbabf2 100644
--- a/README.md
+++ b/README.md
@@ -31,5 +31,4 @@ Issues:
Notes:
- **< input />** is sort-of inline in normal web. We render it as a block element (new-line).
-- A single `RichTextLabel` for inline text tags should stop, we should use invididual ones so it's easier to style and achieve separation through a `vboxcontainer`.
- Fonts use **Flash of Unstyled Text (FOUT)** as opposed to **Flash of Invisible Text (FOIT)**, meaning the text with custom fonts will render with a generic font (sans-serif) while the custom ones downloads.
diff --git a/dns/frontend/index.html b/dns/frontend/index.html
index d97961d..3de60b0 100644
--- a/dns/frontend/index.html
+++ b/dns/frontend/index.html
@@ -47,7 +47,7 @@
Log In
-
Don't have an account? Register here
+ Don't have an account? Register here
diff --git a/dns/frontend/register.html b/dns/frontend/register.html
index 95c035c..9402319 100644
--- a/dns/frontend/register.html
+++ b/dns/frontend/register.html
@@ -46,7 +46,7 @@
}
.form-input {
- w-full p-3 border border-gray-600 rounded-md bg-[#374151] text-white active:border-red-500
+ w-64 p-3 border border-gray-600 rounded-md bg-[#374151] text-white active:border-red-500
}
.card {
diff --git a/dns/frontend/register.lua b/dns/frontend/register.lua
index 9a6d90c..29c2f01 100644
--- a/dns/frontend/register.lua
+++ b/dns/frontend/register.lua
@@ -7,6 +7,7 @@ local tldSelector = gurt.select('#tld-selector')
local loadingElement = gurt.select('#tld-loading')
local displayElement = gurt.select('#invite-code-display')
local remainingElement = gurt.select('#remaining')
+local redeemBtn = gurt.select('#redeem-invite-btn')
local options
@@ -169,6 +170,10 @@ local function createInvite()
displayElement.text = 'Invite code: ' .. inviteCode .. ' (copied to clipboard)'
displayElement:show()
Clipboard.write(inviteCode)
+
+ user.registrations_remaining = user.registrations_remaining - 1
+ updateUserInfo()
+
print('Invite code created and copied to clipboard: ' .. inviteCode)
else
print('Failed to create invite: ' .. response:text())
@@ -197,7 +202,11 @@ local function redeemInvite(code)
updateUserInfo()
-- Clear form
- gurt.select('#invite-code-input').text = ''
+ gurt.select('#invite-code-input').value = ''
+ redeemBtn.text = 'Success!'
+ gurt.setTimeout(function()
+ redeemBtn.text = 'Redeem'
+ end, 1000)
else
local error = response:text()
showError('redeem-error', 'Failed to redeem invite: ' .. error)
@@ -236,8 +245,8 @@ end)
gurt.select('#create-invite-btn'):on('click', createInvite)
-gurt.select('#redeem-invite-btn'):on('click', function()
- local code = gurt.select('#invite-code-input').text
+redeemBtn:on('click', function()
+ local code = gurt.select('#invite-code-input').value
if code and code ~= '' then
redeemInvite(code)
end
diff --git a/dns/frontend/signup.html b/dns/frontend/signup.html
index e86ade0..f3680b3 100644
--- a/dns/frontend/signup.html
+++ b/dns/frontend/signup.html
@@ -61,7 +61,7 @@
Create Account
- Already have an account? Login here
+ Already have an account? Login here
diff --git a/dns/src/gurt_server/auth_routes.rs b/dns/src/gurt_server/auth_routes.rs
index b54afdc..c9e6d88 100644
--- a/dns/src/gurt_server/auth_routes.rs
+++ b/dns/src/gurt_server/auth_routes.rs
@@ -197,17 +197,32 @@ pub(crate) async fn create_invite(_ctx: &ServerContext, app_state: AppState, cla
.collect()
};
- // Insert invite code into database
- let insert_result = sqlx::query(
- "INSERT INTO invite_codes (code, created_by, created_at) VALUES ($1, $2, $3)"
- )
- .bind(&invite_code)
- .bind(claims.user_id)
- .bind(Utc::now())
- .execute(&app_state.db)
- .await;
+ let mut tx = app_state.db.begin().await
+ .map_err(|_| GurtError::invalid_message("Database error"))?;
- match insert_result {
+ let affected_rows = sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1 AND registrations_remaining > 0")
+ .bind(claims.user_id)
+ .execute(&mut *tx)
+ .await
+ .map_err(|_| GurtError::invalid_message("Database error"))?
+ .rows_affected();
+
+ if affected_rows == 0 {
+ return Ok(GurtResponse::bad_request().with_json_body(&Error {
+ msg: "No registrations remaining to create invite",
+ error: "INSUFFICIENT_REGISTRATIONS".into(),
+ })?);
+ }
+
+ sqlx::query("INSERT INTO invite_codes (code, created_by, created_at) VALUES ($1, $2, $3)")
+ .bind(&invite_code)
+ .bind(claims.user_id)
+ .bind(Utc::now())
+ .execute(&mut *tx)
+ .await
+ .map_err(|_| GurtError::invalid_message("Database error"))?;
+
+ match tx.commit().await {
Ok(_) => {
let response = serde_json::json!({
"invite_code": invite_code
@@ -252,7 +267,7 @@ pub(crate) async fn redeem_invite(ctx: &ServerContext, app_state: AppState, clai
.await
.map_err(|_| GurtError::invalid_message("Database error"))?;
- sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining + 3 WHERE id = $1")
+ sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining + 1 WHERE id = $1")
.bind(claims.user_id)
.execute(&mut *tx)
.await
@@ -262,7 +277,7 @@ pub(crate) async fn redeem_invite(ctx: &ServerContext, app_state: AppState, clai
.map_err(|_| GurtError::invalid_message("Database error"))?;
let response = serde_json::json!({
- "registrations_added": 3
+ "registrations_added": 1
});
Ok(GurtResponse::ok().with_json_body(&response)?)
}
@@ -282,20 +297,6 @@ pub(crate) async fn redeem_invite(ctx: &ServerContext, app_state: AppState, clai
}
pub(crate) async fn create_domain_invite(_ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result {
- // Check if user has domain invite codes remaining
- let user: (i32,) = sqlx::query_as("SELECT domain_invite_codes FROM users WHERE id = $1")
- .bind(claims.user_id)
- .fetch_one(&app_state.db)
- .await
- .map_err(|_| GurtError::invalid_message("User not found"))?;
-
- if user.0 <= 0 {
- return Ok(GurtResponse::bad_request().with_json_body(&Error {
- msg: "No domain invite codes remaining",
- error: "NO_INVITES_REMAINING".into(),
- })?);
- }
-
// Generate random domain invite code
let invite_code: String = {
use rand::Rng;
@@ -312,6 +313,20 @@ pub(crate) async fn create_domain_invite(_ctx: &ServerContext, app_state: AppSta
let mut tx = app_state.db.begin().await
.map_err(|_| GurtError::invalid_message("Database error"))?;
+ let affected_rows = sqlx::query("UPDATE users SET domain_invite_codes = domain_invite_codes - 1 WHERE id = $1 AND domain_invite_codes > 0")
+ .bind(claims.user_id)
+ .execute(&mut *tx)
+ .await
+ .map_err(|_| GurtError::invalid_message("Database error"))?
+ .rows_affected();
+
+ if affected_rows == 0 {
+ return Ok(GurtResponse::bad_request().with_json_body(&Error {
+ msg: "No domain invite codes remaining",
+ error: "NO_INVITES_REMAINING".into(),
+ })?);
+ }
+
sqlx::query(
"INSERT INTO domain_invite_codes (code, created_by, created_at) VALUES ($1, $2, $3)"
)
@@ -322,12 +337,6 @@ pub(crate) async fn create_domain_invite(_ctx: &ServerContext, app_state: AppSta
.await
.map_err(|_| GurtError::invalid_message("Database error"))?;
- sqlx::query("UPDATE users SET domain_invite_codes = domain_invite_codes - 1 WHERE id = $1")
- .bind(claims.user_id)
- .execute(&mut *tx)
- .await
- .map_err(|_| GurtError::invalid_message("Database error"))?;
-
tx.commit().await
.map_err(|_| GurtError::invalid_message("Database error"))?;
diff --git a/dns/src/gurt_server/routes.rs b/dns/src/gurt_server/routes.rs
index 5b8031d..76fafc9 100644
--- a/dns/src/gurt_server/routes.rs
+++ b/dns/src/gurt_server/routes.rs
@@ -85,14 +85,23 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) -
let domain_id = domain_row.0;
- // Decrease user's registrations remaining
- sqlx::query(
- "UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1",
+ let affected_rows = sqlx::query(
+ "UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1 AND registrations_remaining > 0",
)
.bind(user_id)
.execute(&app.db)
.await
- .map_err(|_| GurtError::invalid_message("Failed to update user registrations"))?;
+ .map_err(|_| GurtError::invalid_message("Failed to update user registrations"))?
+ .rows_affected();
+
+ if affected_rows == 0 {
+ sqlx::query("DELETE FROM domains WHERE id = $1")
+ .bind(domain_id)
+ .execute(&app.db)
+ .await
+ .map_err(|_| GurtError::invalid_message("Database cleanup error"))?;
+ return Err(GurtError::invalid_message("No registrations remaining"));
+ }
if !app.config.discord.bot_token.is_empty() && app.config.discord.channel_id != 0 {
let domain_registration = DomainRegistration {
@@ -123,20 +132,6 @@ pub(crate) async fn create_domain(
app_state: AppState,
claims: Claims,
) -> Result {
- // Check if user has registrations remaining
- let user: (i32,) = sqlx::query_as("SELECT registrations_remaining FROM users WHERE id = $1")
- .bind(claims.user_id)
- .fetch_one(&app_state.db)
- .await
- .map_err(|_| GurtError::invalid_message("User not found"))?;
-
- if user.0 <= 0 {
- return Ok(GurtResponse::bad_request().with_json_body(&Error {
- msg: "Failed to create domain",
- error: "No registrations remaining".into(),
- })?);
- }
-
let domain: Domain = serde_json::from_slice(ctx.body())
.map_err(|_| GurtError::invalid_message("Invalid JSON"))?;
diff --git a/flumi/Scenes/Tags/p.tscn b/flumi/Scenes/Tags/p.tscn
index a529b6e..7603fb0 100644
--- a/flumi/Scenes/Tags/p.tscn
+++ b/flumi/Scenes/Tags/p.tscn
@@ -1,21 +1,12 @@
-[gd_scene load_steps=3 format=3 uid="uid://fr6lovy4ikok"]
+[gd_scene load_steps=2 format=3 uid="uid://fr6lovy4ikok"]
[ext_resource type="Script" uid="uid://cg6kjvlx3an1j" path="res://Scripts/Tags/p.gd" id="1_pnbfg"]
-[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_1glvj"]
-[node name="p" type="RichTextLabel"]
+[node name="p" type="HBoxContainer"]
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 19.0
grow_horizontal = 2
size_flags_horizontal = 3
-focus_mode = 2
-mouse_default_cursor_shape = 1
-theme = ExtResource("2_1glvj")
-theme_override_colors/default_color = Color(0, 0, 0, 1)
-bbcode_enabled = true
-text = "Placeholder"
-fit_content = true
-vertical_alignment = 1
-selection_enabled = true
+size_flags_vertical = 8
script = ExtResource("1_pnbfg")
diff --git a/flumi/Scripts/B9/HTMLParser.gd b/flumi/Scripts/B9/HTMLParser.gd
index 58b9086..86d9bed 100644
--- a/flumi/Scripts/B9/HTMLParser.gd
+++ b/flumi/Scripts/B9/HTMLParser.gd
@@ -436,7 +436,9 @@ static func apply_element_bbcode_formatting(element: HTMLElement, styles: Dictio
color = "#" + color.to_html(false)
else:
color = str(color)
- formatted_content = "[color=%s]%s[/color]" % [color, formatted_content]
+ var color_pattern = "[color=%s]" % color
+ if not formatted_content.contains(color_pattern):
+ formatted_content = "[color=%s]%s[/color]" % [color, formatted_content]
# Apply tag-specific formatting
match element.tag_name:
@@ -471,8 +473,8 @@ static func apply_element_bbcode_formatting(element: HTMLElement, styles: Dictio
var href = element.get_attribute("href")
if href.length() > 0:
- # Pass raw href - URL resolution happens in handle_link_click
- formatted_content = "[url=%s]%s[/url]" % [href, formatted_content]
+ if not formatted_content.contains("[url="):
+ formatted_content = "[url=%s]%s[/url]" % [href, formatted_content]
return formatted_content
diff --git a/flumi/Scripts/B9/Lua.gd b/flumi/Scripts/B9/Lua.gd
index 715e2d8..e04ca64 100644
--- a/flumi/Scripts/B9/Lua.gd
+++ b/flumi/Scripts/B9/Lua.gd
@@ -489,6 +489,10 @@ func _on_input_focus_lost(subscription: EventSubscription) -> void:
current_text = dom_node.get_text()
elif "text" in dom_node:
current_text = dom_node.text
+ else:
+ var element = dom_parser.find_by_id(subscription.element_id)
+ if element:
+ current_text = element.text_content
var event_info = {"value": current_text}
_execute_lua_callback(subscription, [event_info])
@@ -723,10 +727,7 @@ func _handle_text_setting(operation: Dictionary):
var element = SelectorUtils.find_first_matching(selector, dom_parser.parse_result.all_elements)
if element:
- # Always update the HTML element's text content first
- element.text_content = text
-
- # If the element has a DOM node, update it too
+ # If the element has a DOM node, update it directly without updating text_content
var element_id = get_or_assign_element_id(element)
var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null)
if dom_node:
@@ -734,24 +735,37 @@ func _handle_text_setting(operation: Dictionary):
var button_node = dom_node.get_node_or_null("ButtonNode")
if button_node and button_node is Button:
button_node.text = text
+ element.text_content = text
return
+ if element.tag_name == "p" and dom_node.has_method("set_text"):
+ dom_node.set_text(text)
+ element.text_content = text
+ return
+
+ element.text_content = text
+
+ if dom_node:
var text_node = get_dom_node(dom_node, "text")
if text_node:
if text_node is RichTextLabel:
StyleManager.apply_styles_to_label(text_node, dom_parser.get_element_styles_with_inheritance(element, "", []), element, dom_parser, text)
- text_node.call_deferred("_auto_resize_to_content")
+ try_apply_auto_resize(text_node)
elif text_node.has_method("set_text"):
text_node.set_text(text)
elif "text" in text_node:
text_node.text = text
- if text_node.has_method("_auto_resize_to_content"):
- text_node.call_deferred("_auto_resize_to_content")
+ try_apply_auto_resize(text_node)
else:
var rich_text_label = _find_rich_text_label_recursive(dom_node)
if rich_text_label:
StyleManager.apply_styles_to_label(rich_text_label, dom_parser.get_element_styles_with_inheritance(element, "", []), element, dom_parser, text)
- rich_text_label.call_deferred("_auto_resize_to_content")
+ try_apply_auto_resize(rich_text_label)
+
+func try_apply_auto_resize(text_node: Node) -> void:
+ var parent = text_node.get_parent()
+ if parent and parent.has_method("_apply_auto_resize_to_label"):
+ parent.call_deferred("_apply_auto_resize_to_label", text_node)
func _find_rich_text_label_recursive(node: Node) -> RichTextLabel:
if node is RichTextLabel:
diff --git a/flumi/Scripts/Tags/input.gd b/flumi/Scripts/Tags/input.gd
index 740442f..17b824d 100644
--- a/flumi/Scripts/Tags/input.gd
+++ b/flumi/Scripts/Tags/input.gd
@@ -464,17 +464,17 @@ func apply_input_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
var new_height = max(active_child.custom_minimum_size.y, active_child.size.y)
if width:
- if width == "100%":
+ if typeof(width) == TYPE_STRING and width == "100%":
active_child.size_flags_horizontal = Control.SIZE_EXPAND_FILL
size_flags_horizontal = Control.SIZE_EXPAND_FILL
new_width = 0
- elif SizingUtils.is_percentage(width):
+ elif typeof(width) == TYPE_STRING and SizingUtils.is_percentage(width):
new_width = SizingUtils.calculate_percentage_size(width, SizingUtils.DEFAULT_VIEWPORT_WIDTH)
else:
new_width = width
if height:
- if SizingUtils.is_percentage(height):
+ if typeof(height) == TYPE_STRING and SizingUtils.is_percentage(height):
new_height = SizingUtils.calculate_percentage_size(height, SizingUtils.DEFAULT_VIEWPORT_HEIGHT)
else:
new_height = height
@@ -483,7 +483,7 @@ func apply_input_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
active_child.custom_minimum_size = new_child_size
- if width and width != "100%":
+ if width and not (typeof(width) == TYPE_STRING and width == "100%"):
active_child.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
if height:
active_child.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
@@ -494,7 +494,7 @@ func apply_input_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
custom_minimum_size = new_child_size
# Root Control adjusts size flags to match child
- if width and width != "100%":
+ if width and not (typeof(width) == TYPE_STRING and width == "100%"):
size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
else:
size_flags_horizontal = Control.SIZE_EXPAND_FILL
diff --git a/flumi/Scripts/Tags/p.gd b/flumi/Scripts/Tags/p.gd
index fca015b..c8f24d1 100644
--- a/flumi/Scripts/Tags/p.gd
+++ b/flumi/Scripts/Tags/p.gd
@@ -1,75 +1,127 @@
class_name HTMLP
-extends RichTextLabel
+extends HBoxContainer
-var element_styles: Dictionary = {}
+var _element: HTMLParser.HTMLElement
+var _parser: HTMLParser
func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
- element_styles = parser.get_element_styles_with_inheritance(element, "", [])
-
- text = "[font_size=24]%s[/font_size]" % element.get_bbcode_formatted_text(parser)
-
- # Allow mouse events to pass through to parent containers for hover effects while keeping text selection
- mouse_filter = Control.MOUSE_FILTER_PASS
-
- autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
-
- call_deferred("_auto_resize_to_content")
-
+ _element = element
+ _parser = parser
size_flags_horizontal = Control.SIZE_EXPAND_FILL
- set_anchors_and_offsets_preset(Control.PRESET_TOP_WIDE)
+ size_flags_vertical = Control.SIZE_SHRINK_BEGIN
-func _auto_resize_to_content():
- if not is_inside_tree():
- await tree_entered
+ if get_child_count() > 0:
+ return
+
+ var content_parts = []
+ var current_text = ""
+
+ var element_text = element.text_content
+ var child_texts = []
+
+ for child in element.children:
+ child_texts.append(child.text_content)
+
+ var parent_only_text = element_text
+ for child_text in child_texts:
+ parent_only_text = parent_only_text.replace(child_text, "")
+
+ if not parent_only_text.strip_edges().is_empty():
+ var parent_label = create_styled_label(parent_only_text.strip_edges(), element, parser)
+
+ for child in element.children:
+ var child_label = create_styled_label(child.get_bbcode_formatted_text(parser), element, parser)
+
+ if contains_hyperlink(child):
+ child_label.meta_clicked.connect(_on_meta_clicked)
+
+func create_styled_label(text: String, element: HTMLParser.HTMLElement, parser: HTMLParser) -> RichTextLabel:
+ var label = RichTextLabel.new()
+ label.fit_content = true
+ label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
+ label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
+ label.bbcode_enabled = true
+ add_child(label)
+
+ var styles = parser.get_element_styles_with_inheritance(element, "", [])
+ StyleManager.apply_styles_to_label(label, styles, element, parser, text)
+
+ call_deferred("_apply_auto_resize_to_label", label)
+ return label
+
+func _apply_auto_resize_to_label(label: RichTextLabel):
+ if not label.is_inside_tree():
+ await label.tree_entered
var min_width = 20
var max_width = 800
var min_height = 30
- fit_content = true
+ label.fit_content = true
- var original_autowrap = autowrap_mode
- autowrap_mode = TextServer.AUTOWRAP_OFF
+ var original_autowrap = label.autowrap_mode
+ label.autowrap_mode = TextServer.AUTOWRAP_OFF
await get_tree().process_frame
- var natural_width = size.x
-
- var font_weight_multiplier = _get_font_weight_multiplier()
- natural_width *= font_weight_multiplier
+ var natural_width = label.size.x
+ natural_width *= 1.0 # font weight multiplier simplified
var desired_width = clampf(natural_width, min_width, max_width)
- autowrap_mode = original_autowrap
+ label.autowrap_mode = original_autowrap
await get_tree().process_frame
- var content_height = get_content_height()
- var explicit_height = custom_minimum_size.y if custom_minimum_size.y > 0 else 0.0
- var final_height = explicit_height if explicit_height > 0 else max(content_height, min_height)
- custom_minimum_size = Vector2(desired_width, final_height)
+ label.custom_minimum_size = Vector2(desired_width, 0)
- queue_redraw()
+ label.queue_redraw()
-func _get_font_weight_multiplier() -> float:
- #if element_styles.has("font-black"):
- # return 1.12
- #elif element_styles.has("font-extrabold"):
- # return 1.10
- #elif element_styles.has("font-bold"):
- # return 1.04
- #elif element_styles.has("font-semibold"):
- # return 1.06
- #elif element_styles.has("font-medium"):
- # return 1.03
- #elif element_styles.has("font-light"):
- # return 0.98
- #elif element_styles.has("font-extralight") or element_styles.has("font-thin"):
- # return 0.95
+func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
+ if element.tag_name == "a":
+ return true
- #var text_content = get_parsed_text()
+ for child in element.children:
+ if contains_hyperlink(child):
+ return true
- #if text_content.contains("[b]"):
- # return 1.08
+ return false
+
+func _on_meta_clicked(meta: Variant) -> void:
+ var current = get_parent()
+ while current:
+ if current.has_method("handle_link_click"):
+ current.handle_link_click(meta)
+ break
+ current = current.get_parent()
+
+func get_text() -> String:
+ var text_parts = []
+ for child in get_children():
+ if child is RichTextLabel:
+ text_parts.append(child.get_parsed_text())
+ return " ".join(text_parts)
+
+func set_text(new_text: String) -> void:
+ # Clear existing children immediately
+ for child in get_children():
+ remove_child(child)
+ child.queue_free()
- return 1.0
+ if _element and _parser:
+ var label = create_styled_label(new_text, _element, _parser)
+ else:
+ var label = create_label(new_text)
+
+func create_label(text: String) -> RichTextLabel:
+ var label = RichTextLabel.new()
+ label.text = text
+ label.fit_content = true
+ label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
+ label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
+ label.bbcode_enabled = true
+ add_child(label)
+ call_deferred("_apply_auto_resize_to_label", label)
+ return label