Initial WhaleTown V2 frontend

This commit is contained in:
2026-07-19 22:42:20 +08:00
commit 435c578dde
1421 changed files with 54486 additions and 0 deletions

View File

@@ -0,0 +1,647 @@
extends Control
# ============================================================================
# FriendListPanel.gd - 右下角好友列表面板
# ============================================================================
# 独立展示好友在线、好友请求和好友选择。点击好友后通过事件驱动 ChatUI
# 切换到对应好友会话,不在面板内承载聊天内容。
# ============================================================================
const PANEL_SIZE: Vector2 = Vector2(384, 492)
const PANEL_MARGIN: Vector2 = Vector2(32, 32)
const ONLINE_COLOR: Color = Color(0.286, 0.765, 0.486)
const OFFLINE_COLOR: Color = Color(0.678, 0.725, 0.773)
const TEXT_COLOR: Color = Color(0.188, 0.294, 0.424)
const MUTED_COLOR: Color = Color(0.560, 0.639, 0.733)
const ACCENT_COLOR: Color = Color(0.258824, 0.627451, 0.913725)
const SURFACE_COLOR: Color = Color(1.0, 0.996, 0.984, 0.97)
const SURFACE_RAISED_COLOR: Color = Color(1.0, 1.0, 1.0, 0.9)
const BORDER_COLOR: Color = Color(0.788, 0.851, 0.910, 0.52)
const FRIEND_AVATAR_SIZE: Vector2 = Vector2(48, 48)
const FRIEND_AVATAR_CORNER_RADIUS: int = 12
@export var autoRequestFriendList: bool = true
var _panel: PanelContainer
var _titleLabel: Label
var _summaryLabel: Label
var _closeButton: Button
var _requestList: VBoxContainer
var _friendList: VBoxContainer
var _statusLabel: Label
var _friends: Array[Dictionary] = []
var _requests: Array[Dictionary] = []
var _supported: bool = true
var _statusMessage: String = ""
var _isOpen: bool = false
var _hasRequestedFriendList: bool = false
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
_build_ui()
set_panel_open(false)
_subscribe_to_events()
func _exit_tree() -> void:
var eventSystem := _get_event_system()
if eventSystem != null:
eventSystem.call("disconnect_event", EventNames.CHAT_FRIENDS_UPDATED, _on_friends_updated, self)
eventSystem.call("disconnect_event", EventNames.HUD_FRIEND_LIST_TOGGLE, _on_friend_list_toggle, self)
eventSystem.call("disconnect_event", EventNames.SETTINGS_CHANGED, _on_settings_changed, self)
func set_panel_open(open: bool) -> void:
_isOpen = open
if is_instance_valid(_panel):
_panel.visible = _isOpen
if _isOpen and autoRequestFriendList and not _hasRequestedFriendList:
_request_initial_friend_list()
func is_panel_open() -> bool:
return _isOpen
func toggle_panel() -> void:
set_panel_open(not _isOpen)
func _build_ui() -> void:
_panel = PanelContainer.new()
_panel.name = "FriendListCard"
_panel.custom_minimum_size = PANEL_SIZE
_panel.mouse_filter = Control.MOUSE_FILTER_STOP
_panel.add_theme_stylebox_override("panel", _create_panel_style())
add_child(_panel)
_anchor_panel()
var margin := MarginContainer.new()
margin.add_theme_constant_override("margin_left", 18)
margin.add_theme_constant_override("margin_top", 18)
margin.add_theme_constant_override("margin_right", 18)
margin.add_theme_constant_override("margin_bottom", 18)
_panel.add_child(margin)
var content := VBoxContainer.new()
content.add_theme_constant_override("separation", 12)
margin.add_child(content)
var header := HBoxContainer.new()
header.custom_minimum_size = Vector2(0, 48)
header.add_theme_constant_override("separation", 12)
content.add_child(header)
var titleStack := VBoxContainer.new()
titleStack.size_flags_horizontal = Control.SIZE_EXPAND_FILL
titleStack.alignment = BoxContainer.ALIGNMENT_CENTER
titleStack.add_theme_constant_override("separation", 1)
header.add_child(titleStack)
_titleLabel = Label.new()
_titleLabel.text = "好友"
_titleLabel.add_theme_color_override("font_color", TEXT_COLOR)
_titleLabel.add_theme_font_size_override("font_size", 22)
titleStack.add_child(_titleLabel)
var subtitleLabel := Label.new()
subtitleLabel.text = "和鲸落镇的伙伴保持联系"
subtitleLabel.add_theme_color_override("font_color", MUTED_COLOR)
subtitleLabel.add_theme_font_size_override("font_size", 12)
titleStack.add_child(subtitleLabel)
_closeButton = Button.new()
_closeButton.text = "×"
_closeButton.tooltip_text = "收起好友列表"
_closeButton.custom_minimum_size = Vector2(36, 36)
_closeButton.focus_mode = Control.FOCUS_NONE
_closeButton.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
_closeButton.add_theme_font_size_override("font_size", 22)
_closeButton.add_theme_color_override("font_color", MUTED_COLOR)
_closeButton.add_theme_color_override("font_hover_color", ACCENT_COLOR)
_closeButton.add_theme_color_override("font_pressed_color", ACCENT_COLOR.darkened(0.12))
_closeButton.add_theme_color_override("font_disabled_color", Color(0.37, 0.48, 0.58, 0.55))
_closeButton.add_theme_stylebox_override("normal", _create_empty_style())
_closeButton.add_theme_stylebox_override("hover", _create_pill_style(Color(0.918, 0.961, 0.992, 0.96), 12))
_closeButton.add_theme_stylebox_override("pressed", _create_pill_style(Color(0.858, 0.925, 0.980, 1.0), 12))
_closeButton.add_theme_stylebox_override("focus", _create_empty_style())
_closeButton.pressed.connect(func() -> void: set_panel_open(false))
header.add_child(_closeButton)
var summaryCard := PanelContainer.new()
summaryCard.custom_minimum_size = Vector2(0, 54)
summaryCard.add_theme_stylebox_override("panel", _create_summary_style())
content.add_child(summaryCard)
var summaryRow := HBoxContainer.new()
summaryRow.add_theme_constant_override("separation", 10)
summaryCard.add_child(summaryRow)
var onlineDot := PanelContainer.new()
onlineDot.custom_minimum_size = Vector2(10, 10)
onlineDot.size_flags_vertical = Control.SIZE_SHRINK_CENTER
onlineDot.add_theme_stylebox_override("panel", _create_dot_style(ONLINE_COLOR))
summaryRow.add_child(onlineDot)
_summaryLabel = Label.new()
_summaryLabel.text = "暂无在线好友"
_summaryLabel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_summaryLabel.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_summaryLabel.add_theme_color_override("font_color", Color(0.207843, 0.427451, 0.686275))
_summaryLabel.add_theme_font_size_override("font_size", 15)
summaryRow.add_child(_summaryLabel)
var chatHint := Label.new()
chatHint.text = "选择好友开始私聊"
chatHint.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
chatHint.add_theme_color_override("font_color", MUTED_COLOR)
chatHint.add_theme_font_size_override("font_size", 12)
summaryRow.add_child(chatHint)
_statusLabel = Label.new()
_statusLabel.visible = false
_statusLabel.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_statusLabel.add_theme_color_override("font_color", MUTED_COLOR)
_statusLabel.add_theme_font_size_override("font_size", 15)
content.add_child(_statusLabel)
_requestList = VBoxContainer.new()
_requestList.add_theme_constant_override("separation", 8)
content.add_child(_requestList)
var friendsHeading := Label.new()
friendsHeading.text = "全部好友"
friendsHeading.add_theme_color_override("font_color", MUTED_COLOR)
friendsHeading.add_theme_font_size_override("font_size", 13)
content.add_child(friendsHeading)
var scroll := ScrollContainer.new()
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
scroll.add_theme_stylebox_override("panel", _create_empty_style())
content.add_child(scroll)
_friendList = VBoxContainer.new()
_friendList.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_friendList.add_theme_constant_override("separation", 8)
scroll.add_child(_friendList)
_render()
func _anchor_panel() -> void:
_panel.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
_panel.offset_left = -PANEL_SIZE.x - PANEL_MARGIN.x
_panel.offset_top = -PANEL_SIZE.y - PANEL_MARGIN.y
_panel.offset_right = -PANEL_MARGIN.x
_panel.offset_bottom = -PANEL_MARGIN.y
_panel.grow_horizontal = Control.GROW_DIRECTION_BEGIN
_panel.grow_vertical = Control.GROW_DIRECTION_BEGIN
func _subscribe_to_events() -> void:
var eventSystem := _get_event_system()
if eventSystem == null:
push_warning("FriendListPanel: EventSystem autoload is not available.")
return
eventSystem.call("connect_event", EventNames.CHAT_FRIENDS_UPDATED, _on_friends_updated, self)
eventSystem.call("connect_event", EventNames.HUD_FRIEND_LIST_TOGGLE, _on_friend_list_toggle, self)
eventSystem.call("connect_event", EventNames.SETTINGS_CHANGED, _on_settings_changed, self)
func _request_initial_friend_list() -> void:
_hasRequestedFriendList = true
var chatManager := get_node_or_null("/root/ChatManager")
if chatManager != null and chatManager.has_method("request_friend_list"):
chatManager.call_deferred("request_friend_list")
func _on_friend_list_toggle(_data: Variant = null) -> void:
toggle_panel()
func _on_friends_updated(data: Dictionary) -> void:
_friends.clear()
_requests.clear()
_supported = bool(data.get("supported", true))
_statusMessage = str(data.get("status", "")).strip_edges()
var friendsVariant: Variant = data.get("friends", [])
if friendsVariant is Array:
for friendVariant in friendsVariant:
if friendVariant is Dictionary:
_friends.append(friendVariant)
var requestsVariant: Variant = data.get("requests", [])
if requestsVariant is Array:
for requestVariant in requestsVariant:
if requestVariant is Dictionary:
_requests.append(requestVariant)
_render()
func _on_settings_changed(_data: Dictionary) -> void:
_render()
func _render() -> void:
if not is_instance_valid(_friendList) or not is_instance_valid(_requestList):
return
_clear_children(_friendList)
_clear_children(_requestList)
var onlineCount := _count_online_friends()
_titleLabel.text = "好友"
if is_instance_valid(_summaryLabel):
if _friends.is_empty():
_summaryLabel.text = "暂无好友,去广场认识新伙伴吧"
else:
_summaryLabel.text = "%d 位好友在线,共 %d 位伙伴" % [onlineCount, _friends.size()]
if not _supported:
_show_status(_statusMessage if not _statusMessage.is_empty() else "当前后端暂不支持好友功能")
return
_statusLabel.visible = false
_render_requests()
_render_friends()
func _render_requests() -> void:
if _requests.is_empty() or not _settings_bool("friend_request_notifications", true):
_requestList.visible = false
return
_requestList.visible = true
var titleCard := PanelContainer.new()
titleCard.custom_minimum_size = Vector2(0, 34)
titleCard.add_theme_stylebox_override("panel", _create_section_style())
_requestList.add_child(titleCard)
var title := Label.new()
title.text = "待处理请求 %d" % _requests.size()
title.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
title.add_theme_color_override("font_color", Color(0.207843, 0.427451, 0.686275))
title.add_theme_font_size_override("font_size", 13)
titleCard.add_child(title)
for request in _requests:
_requestList.add_child(_create_request_row(request))
func _render_friends() -> void:
if _friends.is_empty():
var emptyCard := PanelContainer.new()
emptyCard.custom_minimum_size = Vector2(0, 100)
emptyCard.add_theme_stylebox_override("panel", _create_empty_state_style())
_friendList.add_child(emptyCard)
var emptyLabel := Label.new()
emptyLabel.text = "暂无好友\n靠近玩家按 F 发送好友请求"
emptyLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
emptyLabel.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
emptyLabel.add_theme_color_override("font_color", MUTED_COLOR)
emptyLabel.add_theme_font_size_override("font_size", 14)
emptyCard.add_child(emptyLabel)
return
for friend in _sorted_friends():
_friendList.add_child(_create_friend_row(friend))
func _create_friend_row(friend: Dictionary) -> Control:
var userId := _read_user_id(friend)
var username := str(friend.get("username", "好友")).strip_edges()
if username.is_empty():
username = "好友"
var online := bool(friend.get("online", false))
var button := Button.new()
button.focus_mode = Control.FOCUS_NONE
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
button.disabled = userId.is_empty()
button.custom_minimum_size = Vector2(0, 68)
button.text = ""
button.add_theme_stylebox_override("normal", _create_friend_row_style(false, online))
button.add_theme_stylebox_override("hover", _create_friend_row_style(true, online))
button.add_theme_stylebox_override("pressed", _create_friend_row_style(true, online, true))
button.add_theme_stylebox_override("disabled", _create_friend_row_style(false, false))
button.add_theme_stylebox_override("focus", _create_empty_style())
button.pressed.connect(func() -> void: _select_friend(userId, username, online))
var row := HBoxContainer.new()
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
row.set_anchors_preset(Control.PRESET_FULL_RECT)
row.offset_left = 10
row.offset_top = 8
row.offset_right = -10
row.offset_bottom = -8
row.add_theme_constant_override("separation", 12)
button.add_child(row)
row.add_child(_create_avatar(username, online))
var textBox := VBoxContainer.new()
textBox.mouse_filter = Control.MOUSE_FILTER_IGNORE
textBox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
textBox.alignment = BoxContainer.ALIGNMENT_CENTER
textBox.add_theme_constant_override("separation", 3)
row.add_child(textBox)
var nameLabel := Label.new()
nameLabel.mouse_filter = Control.MOUSE_FILTER_IGNORE
nameLabel.text = username
nameLabel.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
nameLabel.add_theme_color_override("font_color", TEXT_COLOR if online else MUTED_COLOR)
nameLabel.add_theme_font_size_override("font_size", 16)
textBox.add_child(nameLabel)
var statusLabel := Label.new()
statusLabel.mouse_filter = Control.MOUSE_FILTER_IGNORE
statusLabel.text = "正在鲸落镇" if online else "暂时离线"
statusLabel.add_theme_color_override("font_color", ONLINE_COLOR if online else MUTED_COLOR)
statusLabel.add_theme_font_size_override("font_size", 12)
textBox.add_child(statusLabel)
var dot := PanelContainer.new()
dot.name = "FriendOnlineIndicator"
dot.custom_minimum_size = Vector2(8, 8)
dot.size_flags_vertical = Control.SIZE_SHRINK_CENTER
dot.mouse_filter = Control.MOUSE_FILTER_IGNORE
dot.add_theme_stylebox_override("panel", _create_dot_style(ONLINE_COLOR if online else OFFLINE_COLOR))
row.add_child(dot)
return button
func _create_request_row(request: Dictionary) -> Control:
var userId := _read_user_id(request)
var username := str(request.get("username", "玩家")).strip_edges()
if username.is_empty():
username = "玩家"
var rowPanel := PanelContainer.new()
rowPanel.custom_minimum_size = Vector2(0, 48)
rowPanel.add_theme_stylebox_override("panel", _create_request_style())
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 8)
rowPanel.add_child(row)
var nameLabel := Label.new()
nameLabel.mouse_filter = Control.MOUSE_FILTER_IGNORE
nameLabel.text = username
nameLabel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
nameLabel.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
nameLabel.add_theme_color_override("font_color", TEXT_COLOR)
nameLabel.add_theme_font_size_override("font_size", 14)
row.add_child(nameLabel)
var acceptButton := _create_small_button("接受", Color(0.333, 0.761, 0.514))
acceptButton.pressed.connect(func() -> void: _respond_request(userId, username, true))
row.add_child(acceptButton)
var rejectButton := _create_small_button("拒绝", Color(0.812, 0.553, 0.553))
rejectButton.pressed.connect(func() -> void: _respond_request(userId, username, false))
row.add_child(rejectButton)
return rowPanel
func _select_friend(userId: String, username: String, _online: bool) -> void:
if userId.strip_edges().is_empty():
return
var eventSystem := _get_event_system()
if eventSystem != null:
eventSystem.call("emit_event", EventNames.CHAT_FRIEND_SELECTED, {
"user_id": userId,
"username": username
})
func _respond_request(userId: String, username: String, accepted: bool) -> void:
if userId.strip_edges().is_empty():
return
var chatManager := get_node_or_null("/root/ChatManager")
if chatManager == null:
return
var ok := false
if accepted and chatManager.has_method("accept_friend_request"):
ok = bool(chatManager.call("accept_friend_request", userId, username))
elif not accepted and chatManager.has_method("reject_friend_request"):
ok = bool(chatManager.call("reject_friend_request", userId, username))
if ok:
for i in range(_requests.size() - 1, -1, -1):
if _read_user_id(_requests[i]) == userId:
_requests.remove_at(i)
_render()
func _show_status(message: String) -> void:
_statusLabel.text = message
_statusLabel.visible = true
_requestList.visible = false
func _count_online_friends() -> int:
var count := 0
for friend in _friends:
if bool(friend.get("online", false)):
count += 1
return count
func _sorted_friends() -> Array[Dictionary]:
var sortedFriends := _friends.duplicate(true)
sortedFriends.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
var onlineA := bool(a.get("online", false))
var onlineB := bool(b.get("online", false))
if onlineA != onlineB:
return onlineA
return str(a.get("username", "")).nocasecmp_to(str(b.get("username", ""))) < 0
)
return sortedFriends
func _read_user_id(data: Dictionary) -> String:
return str(data.get("user_id", data.get("userId", ""))).strip_edges()
func _create_avatar(username: String, online: bool) -> PanelContainer:
var avatar := PanelContainer.new()
avatar.name = "FriendAvatarPanel"
avatar.custom_minimum_size = FRIEND_AVATAR_SIZE
avatar.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
avatar.size_flags_vertical = Control.SIZE_SHRINK_CENTER
avatar.mouse_filter = Control.MOUSE_FILTER_IGNORE
avatar.add_theme_stylebox_override("panel", _create_avatar_style(online))
var label := Label.new()
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
label.text = _avatar_text(username)
label.custom_minimum_size = FRIEND_AVATAR_SIZE
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
label.add_theme_color_override("font_color", Color.WHITE)
label.add_theme_font_size_override("font_size", 16)
avatar.add_child(label)
return avatar
func _avatar_text(username: String) -> String:
var normalized := username.strip_edges()
if normalized.is_empty():
return ""
return normalized.substr(0, 1).to_upper()
func _create_small_button(text: String, color: Color) -> Button:
var button := Button.new()
button.text = text
button.custom_minimum_size = Vector2(52, 30)
button.focus_mode = Control.FOCUS_NONE
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
button.add_theme_font_size_override("font_size", 13)
button.add_theme_color_override("font_color", Color.WHITE)
button.add_theme_stylebox_override("normal", _create_pill_style(color, 9))
button.add_theme_stylebox_override("hover", _create_pill_style(color.lightened(0.08), 9))
button.add_theme_stylebox_override("pressed", _create_pill_style(color.darkened(0.08), 9))
button.add_theme_stylebox_override("focus", _create_empty_style())
return button
func _create_panel_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = SURFACE_COLOR
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = BORDER_COLOR
style.corner_radius_top_left = 20
style.corner_radius_top_right = 20
style.corner_radius_bottom_left = 20
style.corner_radius_bottom_right = 20
style.shadow_color = Color(0.12549, 0.282353, 0.407843, 0.18)
style.shadow_size = 18
style.shadow_offset = Vector2(0, 7)
return style
func _create_avatar_style(online: bool) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.361, 0.690, 0.914) if online else Color(0.678, 0.725, 0.773)
style.corner_radius_top_left = FRIEND_AVATAR_CORNER_RADIUS
style.corner_radius_top_right = FRIEND_AVATAR_CORNER_RADIUS
style.corner_radius_bottom_left = FRIEND_AVATAR_CORNER_RADIUS
style.corner_radius_bottom_right = FRIEND_AVATAR_CORNER_RADIUS
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(1, 1, 1, 0.82)
return style
func _create_summary_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.918, 0.961, 0.992, 0.96)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.76, 0.86, 0.94, 0.62)
style.corner_radius_top_left = 12
style.corner_radius_top_right = 12
style.corner_radius_bottom_left = 12
style.corner_radius_bottom_right = 12
style.content_margin_left = 14
style.content_margin_right = 14
return style
func _create_dot_style(color: Color) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = color
style.corner_radius_top_left = 5
style.corner_radius_top_right = 5
style.corner_radius_bottom_left = 5
style.corner_radius_bottom_right = 5
return style
func _create_section_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.948, 0.974, 0.996, 0.96)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.78, 0.87, 0.94, 0.54)
style.corner_radius_top_left = 9
style.corner_radius_top_right = 9
style.corner_radius_bottom_left = 9
style.corner_radius_bottom_right = 9
style.content_margin_left = 12
style.content_margin_right = 12
return style
func _create_empty_state_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.965, 0.982, 0.996, 0.86)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.80, 0.88, 0.94, 0.52)
style.corner_radius_top_left = 12
style.corner_radius_top_right = 12
style.corner_radius_bottom_left = 12
style.corner_radius_bottom_right = 12
return style
func _create_request_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(1, 1, 1, 0.94)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.78, 0.86, 0.93, 0.64)
style.corner_radius_top_left = 10
style.corner_radius_top_right = 10
style.corner_radius_bottom_left = 10
style.corner_radius_bottom_right = 10
style.content_margin_left = 10
style.content_margin_top = 8
style.content_margin_right = 10
style.content_margin_bottom = 8
return style
func _create_friend_row_style(isHovered: bool, isOnline: bool, isPressed: bool = false) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = SURFACE_RAISED_COLOR if isOnline else Color(0.965, 0.973, 0.982, 0.86)
if isHovered:
style.bg_color = Color(0.918, 0.961, 0.992, 0.98)
if isPressed:
style.bg_color = style.bg_color.darkened(0.12)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.55, 0.78, 0.95, 0.72) if isHovered else Color(0.80, 0.87, 0.93, 0.56)
style.corner_radius_top_left = 12
style.corner_radius_top_right = 12
style.corner_radius_bottom_left = 12
style.corner_radius_bottom_right = 12
return style
func _create_pill_style(color: Color, radius: int) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = color
style.corner_radius_top_left = radius
style.corner_radius_top_right = radius
style.corner_radius_bottom_left = radius
style.corner_radius_bottom_right = radius
style.content_margin_left = 10
style.content_margin_right = 10
style.content_margin_top = 5
style.content_margin_bottom = 5
return style
func _create_empty_style() -> StyleBoxEmpty:
return StyleBoxEmpty.new()
func _clear_children(container: Node) -> void:
for child in container.get_children():
container.remove_child(child)
child.queue_free()
func _get_event_system() -> Node:
return get_node_or_null("/root/EventSystem")
func _settings_bool(key: String, defaultValue: bool) -> bool:
var settingsManager := get_node_or_null("/root/SettingsManager")
if settingsManager != null and settingsManager.has_method("get_bool"):
return bool(settingsManager.call("get_bool", key))
return defaultValue