648 lines
24 KiB
GDScript
648 lines
24 KiB
GDScript
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
|