- add network NPC synchronization and dialogue interactions - add world bulletin publishing and display - improve authentication, session refresh, and appearance sync - synchronize player direction and movement animations - improve input focus and progressive Web loading - add macOS/Windows builds and deployment configuration - include required fonts, shaders, and runtime assets
533 lines
20 KiB
GDScript
533 lines
20 KiB
GDScript
extends Control
|
||
|
||
## Lightweight public announcement HUD.
|
||
## This panel owns only the public feed; private conversations remain in ChatUI.
|
||
|
||
const PANEL_WIDTH := 560.0
|
||
const COLLAPSED_HEIGHT := 68.0
|
||
const EXPANDED_HEIGHT := 492.0
|
||
const EDGE_MARGIN := 26.0
|
||
const TEXT_COLOR := Color("24476f")
|
||
const MUTED_COLOR := Color("7894b2")
|
||
const ACCENT_COLOR := Color("2d94ed")
|
||
const SURFACE_COLOR := Color(0.985, 0.995, 1.0, 0.97)
|
||
const LINE_COLOR := Color("d9e8f7")
|
||
const MAX_ITEMS := 24
|
||
|
||
const ICON_EMPTY := preload("res://assets/ui/world_bulletin/community/empty_whale.png")
|
||
const ICON_BROADCAST := preload("res://assets/ui/world_bulletin/community/world_bulletin_icon_hd_simple_tight.png")
|
||
const ICON_BANNER := preload("res://assets/ui/world_bulletin/community/town_banner.png")
|
||
const ICON_PIN := preload("res://assets/ui/world_bulletin/community/pinned_note.png")
|
||
const ICON_RECRUIT := preload("res://assets/ui/world_bulletin/community/recruit_whales.png")
|
||
const ICON_SEND_BUTTON := preload("res://assets/ui/world_bulletin/world_bulletin_send_button_final_192.png")
|
||
|
||
var _panel: PanelContainer
|
||
var _surface_root: VBoxContainer
|
||
var _ticker_row: HBoxContainer
|
||
var _ticker: Button
|
||
var _expand_button: Button
|
||
var _content: VBoxContainer
|
||
var _header_title: Label
|
||
var _header_icon: TextureRect
|
||
var _header_banner: TextureRect
|
||
var _entries: VBoxContainer
|
||
var _scroll: ScrollContainer
|
||
var _composer: LineEdit
|
||
var _send_button: Button
|
||
var _expanded := false
|
||
var _announcements: Array[Dictionary] = []
|
||
var _unread := 0
|
||
var _last_preview := "暂无新的世界公告"
|
||
var _publish_pending := false
|
||
|
||
func _ready() -> void:
|
||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_build_ui()
|
||
_seed_preview_items()
|
||
_subscribe_to_events()
|
||
_set_expanded(true, true)
|
||
|
||
func _exit_tree() -> void:
|
||
var event_system := get_node_or_null("/root/EventSystem")
|
||
if event_system != null:
|
||
event_system.call("disconnect_event", EventNames.CHAT_MESSAGE_RECEIVED, _on_chat_message_received, self)
|
||
event_system.call("disconnect_event", EventNames.CHAT_MESSAGE_SENT, _on_chat_message_sent, self)
|
||
event_system.call("disconnect_event", EventNames.CHAT_ERROR_OCCURRED, _on_chat_error, self)
|
||
|
||
func _input(event: InputEvent) -> void:
|
||
if event is InputEventKey:
|
||
var key_event := event as InputEventKey
|
||
if key_event.pressed and not key_event.echo and key_event.keycode == KEY_B and not _composer.has_focus():
|
||
_set_expanded(not _expanded)
|
||
get_viewport().set_input_as_handled()
|
||
|
||
func _build_ui() -> void:
|
||
_panel = PanelContainer.new()
|
||
_panel.name = "WorldBulletinSurface"
|
||
_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||
_panel.add_theme_stylebox_override("panel", _surface_style())
|
||
add_child(_panel)
|
||
_panel.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
|
||
_panel.offset_left = EDGE_MARGIN
|
||
_panel.offset_top = -EDGE_MARGIN - COLLAPSED_HEIGHT
|
||
_panel.offset_right = EDGE_MARGIN + PANEL_WIDTH
|
||
_panel.offset_bottom = -EDGE_MARGIN
|
||
_panel.grow_horizontal = Control.GROW_DIRECTION_END
|
||
_panel.grow_vertical = Control.GROW_DIRECTION_BEGIN
|
||
_surface_root = VBoxContainer.new()
|
||
_surface_root.name = "SurfaceContent"
|
||
_surface_root.add_theme_constant_override("separation", 0)
|
||
_surface_root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_surface_root.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_panel.add_child(_surface_root)
|
||
|
||
_ticker_row = HBoxContainer.new()
|
||
_ticker_row.name = "CollapsedTickerRow"
|
||
_ticker_row.custom_minimum_size = Vector2(PANEL_WIDTH, COLLAPSED_HEIGHT)
|
||
_ticker_row.add_theme_constant_override("separation", 4)
|
||
_ticker_row.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_surface_root.add_child(_ticker_row)
|
||
|
||
_ticker = Button.new()
|
||
_ticker.name = "CollapsedTicker"
|
||
_ticker.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_ticker.custom_minimum_size = Vector2(0, COLLAPSED_HEIGHT)
|
||
_ticker.focus_mode = Control.FOCUS_NONE
|
||
_ticker.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||
_ticker.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||
_ticker.add_theme_font_size_override("font_size", 20)
|
||
_ticker.add_theme_color_override("font_color", TEXT_COLOR)
|
||
_ticker.add_theme_color_override("font_hover_color", TEXT_COLOR)
|
||
_ticker.add_theme_stylebox_override("normal", _empty_style())
|
||
_ticker.add_theme_stylebox_override("hover", _hover_style())
|
||
_ticker.add_theme_stylebox_override("pressed", _pressed_style())
|
||
_ticker.pressed.connect(func() -> void: _set_expanded(not _expanded))
|
||
_ticker_row.add_child(_ticker)
|
||
|
||
_expand_button = Button.new()
|
||
_expand_button.name = "ExpandButton"
|
||
_expand_button.text = "展开"
|
||
_expand_button.custom_minimum_size = Vector2(72, COLLAPSED_HEIGHT)
|
||
_expand_button.focus_mode = Control.FOCUS_NONE
|
||
_expand_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||
_expand_button.add_theme_font_size_override("font_size", 18)
|
||
_expand_button.add_theme_color_override("font_color", ACCENT_COLOR)
|
||
_expand_button.add_theme_color_override("font_hover_color", TEXT_COLOR)
|
||
_expand_button.add_theme_stylebox_override("normal", _empty_style())
|
||
_expand_button.add_theme_stylebox_override("hover", _hover_style())
|
||
_expand_button.add_theme_stylebox_override("pressed", _pressed_style())
|
||
_expand_button.pressed.connect(func() -> void: _set_expanded(true))
|
||
_ticker_row.add_child(_expand_button)
|
||
|
||
_content = VBoxContainer.new()
|
||
_content.name = "ExpandedContent"
|
||
_content.add_theme_constant_override("separation", 0)
|
||
_content.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_surface_root.add_child(_content)
|
||
|
||
var header := HBoxContainer.new()
|
||
header.name = "Header"
|
||
header.custom_minimum_size = Vector2(0, 74)
|
||
header.add_theme_constant_override("separation", 12)
|
||
header.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_content.add_child(header)
|
||
|
||
_header_icon = TextureRect.new()
|
||
_header_icon.custom_minimum_size = Vector2(56, 56)
|
||
_header_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
_header_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
_header_icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||
_header_icon.texture = ICON_BROADCAST
|
||
_header_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
header.add_child(_header_icon)
|
||
|
||
_header_title = Label.new()
|
||
_header_title.text = "世界公告"
|
||
_header_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_header_title.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||
_header_title.add_theme_font_size_override("font_size", 26)
|
||
_header_title.add_theme_color_override("font_color", TEXT_COLOR)
|
||
_header_title.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
header.add_child(_header_title)
|
||
|
||
_header_banner = TextureRect.new()
|
||
_header_banner.name = "TownBanner"
|
||
_header_banner.custom_minimum_size = Vector2(88, 58)
|
||
_header_banner.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
_header_banner.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
_header_banner.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||
_header_banner.texture = ICON_BANNER
|
||
_header_banner.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
header.add_child(_header_banner)
|
||
|
||
var close_button := Button.new()
|
||
close_button.text = "×"
|
||
close_button.tooltip_text = "收起世界公告"
|
||
close_button.custom_minimum_size = Vector2(54, 54)
|
||
close_button.focus_mode = Control.FOCUS_NONE
|
||
close_button.add_theme_font_size_override("font_size", 34)
|
||
close_button.add_theme_color_override("font_color", MUTED_COLOR)
|
||
close_button.add_theme_color_override("font_hover_color", ACCENT_COLOR)
|
||
close_button.add_theme_stylebox_override("normal", _empty_style())
|
||
close_button.add_theme_stylebox_override("hover", _hover_style())
|
||
close_button.add_theme_stylebox_override("pressed", _pressed_style())
|
||
close_button.pressed.connect(func() -> void: _set_expanded(false))
|
||
header.add_child(close_button)
|
||
|
||
var header_rule := ColorRect.new()
|
||
header_rule.custom_minimum_size = Vector2(0, 1)
|
||
header_rule.color = LINE_COLOR
|
||
header_rule.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_content.add_child(header_rule)
|
||
|
||
_scroll = ScrollContainer.new()
|
||
_scroll.name = "AnnouncementScroll"
|
||
_scroll.custom_minimum_size = Vector2(0, 320)
|
||
_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||
_scroll.mouse_filter = Control.MOUSE_FILTER_STOP
|
||
_content.add_child(_scroll)
|
||
|
||
_entries = VBoxContainer.new()
|
||
_entries.name = "AnnouncementEntries"
|
||
_entries.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_entries.add_theme_constant_override("separation", 0)
|
||
_scroll.add_child(_entries)
|
||
|
||
var composer_row := HBoxContainer.new()
|
||
composer_row.name = "Composer"
|
||
composer_row.custom_minimum_size = Vector2(0, 72)
|
||
composer_row.add_theme_constant_override("separation", 10)
|
||
_content.add_child(composer_row)
|
||
|
||
_composer = LineEdit.new()
|
||
_composer.name = "AnnouncementInput"
|
||
_composer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_composer.placeholder_text = "发布世界公告(100鲸币)..."
|
||
_composer.max_length = 160
|
||
_composer.add_theme_font_size_override("font_size", 20)
|
||
_composer.add_theme_color_override("font_color", TEXT_COLOR)
|
||
_composer.add_theme_color_override("font_placeholder_color", MUTED_COLOR)
|
||
_composer.add_theme_stylebox_override("normal", _input_style())
|
||
_composer.add_theme_stylebox_override("focus", _input_focus_style())
|
||
_composer.text_submitted.connect(func(_value: String) -> void: _publish())
|
||
composer_row.add_child(_composer)
|
||
|
||
_send_button = Button.new()
|
||
_send_button.name = "PublishButton"
|
||
_send_button.text = ""
|
||
_send_button.icon = ICON_SEND_BUTTON
|
||
_send_button.expand_icon = true
|
||
_send_button.icon_max_width = 56
|
||
_send_button.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||
_send_button.tooltip_text = "发布世界公告"
|
||
_send_button.custom_minimum_size = Vector2(56, 56)
|
||
_send_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
_send_button.focus_mode = Control.FOCUS_NONE
|
||
_send_button.add_theme_color_override("icon_normal_color", Color.WHITE)
|
||
_send_button.add_theme_color_override("icon_hover_color", Color("eef9ff"))
|
||
_send_button.add_theme_color_override("icon_pressed_color", Color("bedcf5"))
|
||
_send_button.add_theme_stylebox_override("normal", _empty_style())
|
||
_send_button.add_theme_stylebox_override("hover", _empty_style())
|
||
_send_button.add_theme_stylebox_override("pressed", _empty_style())
|
||
_send_button.add_theme_stylebox_override("focus", _empty_style())
|
||
_send_button.pressed.connect(_publish)
|
||
composer_row.add_child(_send_button)
|
||
|
||
func _subscribe_to_events() -> void:
|
||
var event_system := get_node_or_null("/root/EventSystem")
|
||
if event_system == null:
|
||
return
|
||
event_system.call("connect_event", EventNames.CHAT_MESSAGE_RECEIVED, _on_chat_message_received, self)
|
||
event_system.call("connect_event", EventNames.CHAT_MESSAGE_SENT, _on_chat_message_sent, self)
|
||
event_system.call("connect_event", EventNames.CHAT_ERROR_OCCURRED, _on_chat_error, self)
|
||
|
||
func _on_chat_message_sent(data: Dictionary) -> void:
|
||
if not _publish_pending or not bool(data.get("world_bulletin", false)):
|
||
return
|
||
_composer.clear()
|
||
_set_publish_pending(false)
|
||
|
||
func _on_chat_error(data: Dictionary) -> void:
|
||
if not _publish_pending or not bool(data.get("world_bulletin", false)):
|
||
return
|
||
_set_publish_pending(false)
|
||
_composer.grab_focus()
|
||
|
||
func _set_publish_pending(pending: bool) -> void:
|
||
_publish_pending = pending
|
||
_composer.editable = not pending
|
||
_send_button.disabled = pending
|
||
|
||
func _seed_preview_items() -> void:
|
||
_announcements = [
|
||
{"sender": "小海豚", "time": "12:08", "content": "今晚海湾烟花节 20:00 开始", "category": "活动", "pinned": true},
|
||
{"sender": "珊珊酱", "time": "12:12", "content": "需要 2 名采集伙伴一起出海", "category": "招募"},
|
||
{"sender": "海盐", "time": "12:20", "content": "新地图补给点已刷新", "category": "通知"},
|
||
{"sender": "系统", "time": "12:30", "content": "世界频道维护将在 15 分钟后结束", "category": "通知"}
|
||
]
|
||
_last_preview = str(_announcements[0].get("content", _last_preview))
|
||
_render_entries()
|
||
|
||
func _on_chat_message_received(data: Dictionary) -> void:
|
||
# 只展示后端确认并已扣费的世界公告,普通全局聊天不进公告栏。
|
||
if not bool(data.get("world_bulletin", data.get("worldBulletin", false))):
|
||
return
|
||
var scope := str(data.get("scope", data.get("channel", "global"))).to_lower()
|
||
var tab := str(data.get("tab", "world")).to_lower()
|
||
if scope in ["private", "whisper"] or tab in ["private", "whisper", "friends"]:
|
||
return
|
||
var content := str(data.get("content", data.get("message", ""))).strip_edges()
|
||
if content.is_empty():
|
||
return
|
||
var sender := str(data.get("from_user", data.get("username", "玩家"))).strip_edges()
|
||
if sender.is_empty():
|
||
sender = "玩家"
|
||
var category := "通知"
|
||
if sender == "系统" or str(data.get("sender_type", "")).to_lower() == "system":
|
||
category = "通知"
|
||
_announcements.push_front({
|
||
"sender": sender,
|
||
"time": Time.get_time_string_from_system().left(5),
|
||
"content": content,
|
||
"category": category
|
||
})
|
||
while _announcements.size() > MAX_ITEMS:
|
||
_announcements.pop_back()
|
||
_last_preview = content
|
||
if not _expanded:
|
||
_unread += 1
|
||
_render_entries()
|
||
_update_ticker()
|
||
|
||
func _set_expanded(expanded: bool, immediate := false) -> void:
|
||
_expanded = expanded
|
||
if _expanded:
|
||
_unread = 0
|
||
_panel.custom_minimum_size = Vector2(PANEL_WIDTH, EXPANDED_HEIGHT)
|
||
_panel.offset_top = -EDGE_MARGIN - EXPANDED_HEIGHT
|
||
_ticker_row.hide()
|
||
_content.show()
|
||
_render_entries()
|
||
else:
|
||
_panel.custom_minimum_size = Vector2(PANEL_WIDTH, COLLAPSED_HEIGHT)
|
||
_panel.offset_top = -EDGE_MARGIN - COLLAPSED_HEIGHT
|
||
_content.hide()
|
||
_ticker_row.show()
|
||
_update_ticker()
|
||
if immediate:
|
||
_panel.modulate.a = 1.0
|
||
|
||
func _update_ticker() -> void:
|
||
if not is_instance_valid(_ticker):
|
||
return
|
||
var unread_text := " · %d" % _unread if _unread > 0 else ""
|
||
_ticker.text = " 世界公告 %s%s" % [_last_preview.left(25), unread_text]
|
||
|
||
func _render_entries() -> void:
|
||
if not is_instance_valid(_entries):
|
||
return
|
||
for child in _entries.get_children():
|
||
child.queue_free()
|
||
if _announcements.is_empty():
|
||
var empty := VBoxContainer.new()
|
||
empty.custom_minimum_size = Vector2(0, 210)
|
||
empty.alignment = BoxContainer.ALIGNMENT_CENTER
|
||
_entries.add_child(empty)
|
||
var whale := TextureRect.new()
|
||
whale.texture = ICON_EMPTY
|
||
whale.custom_minimum_size = Vector2(0, 92)
|
||
whale.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
whale.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
whale.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||
whale.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
empty.add_child(whale)
|
||
var empty_label := Label.new()
|
||
empty_label.text = "暂无公告"
|
||
empty_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
empty_label.add_theme_color_override("font_color", MUTED_COLOR)
|
||
empty.add_child(empty_label)
|
||
return
|
||
for item in _announcements:
|
||
_entries.add_child(_build_entry(item))
|
||
|
||
func _build_entry(item: Dictionary) -> Control:
|
||
var card := PanelContainer.new()
|
||
card.custom_minimum_size = Vector2(0, 92)
|
||
card.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
card.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
var card_style := StyleBoxFlat.new()
|
||
card_style.bg_color = Color(1, 1, 1, 0.55)
|
||
card_style.border_color = LINE_COLOR
|
||
card_style.set_border_width_all(1)
|
||
card_style.set_corner_radius_all(10)
|
||
card.add_theme_stylebox_override("panel", card_style)
|
||
|
||
var margin := MarginContainer.new()
|
||
margin.add_theme_constant_override("margin_left", 10)
|
||
margin.add_theme_constant_override("margin_top", 7)
|
||
margin.add_theme_constant_override("margin_right", 10)
|
||
margin.add_theme_constant_override("margin_bottom", 7)
|
||
card.add_child(margin)
|
||
|
||
var card_row := HBoxContainer.new()
|
||
card_row.add_theme_constant_override("separation", 8)
|
||
card_row.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
margin.add_child(card_row)
|
||
|
||
var row := VBoxContainer.new()
|
||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
row.add_theme_constant_override("separation", 2)
|
||
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
card_row.add_child(row)
|
||
|
||
var meta := HBoxContainer.new()
|
||
meta.custom_minimum_size = Vector2(0, 30)
|
||
meta.add_theme_constant_override("separation", 6)
|
||
meta.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
row.add_child(meta)
|
||
|
||
if bool(item.get("pinned", false)):
|
||
var pin := TextureRect.new()
|
||
pin.custom_minimum_size = Vector2(28, 28)
|
||
pin.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
pin.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
pin.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||
pin.texture = ICON_PIN
|
||
pin.tooltip_text = "置顶公告"
|
||
pin.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
meta.add_child(pin)
|
||
|
||
var sender := Label.new()
|
||
sender.text = str(item.get("sender", "玩家"))
|
||
sender.add_theme_font_size_override("font_size", 20)
|
||
sender.add_theme_color_override("font_color", TEXT_COLOR)
|
||
meta.add_child(sender)
|
||
|
||
var category := Label.new()
|
||
category.text = str(item.get("category", "通知"))
|
||
category.add_theme_font_size_override("font_size", 16)
|
||
category.add_theme_color_override("font_color", Color.WHITE)
|
||
category.add_theme_stylebox_override("normal", _category_style(category.text))
|
||
meta.add_child(category)
|
||
|
||
if str(item.get("category", "")) == "招募":
|
||
var recruit := TextureRect.new()
|
||
recruit.custom_minimum_size = Vector2(32, 28)
|
||
recruit.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
recruit.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
recruit.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||
recruit.texture = ICON_RECRUIT
|
||
recruit.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
meta.add_child(recruit)
|
||
|
||
var time := Label.new()
|
||
time.text = str(item.get("time", ""))
|
||
time.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
time.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||
time.add_theme_font_size_override("font_size", 16)
|
||
time.add_theme_color_override("font_color", MUTED_COLOR)
|
||
meta.add_child(time)
|
||
|
||
var body := Label.new()
|
||
body.text = str(item.get("content", ""))
|
||
body.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||
body.custom_minimum_size = Vector2(0, 30)
|
||
body.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
body.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||
body.add_theme_font_size_override("font_size", 20)
|
||
body.add_theme_color_override("font_color", TEXT_COLOR)
|
||
body.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
row.add_child(body)
|
||
|
||
return card
|
||
|
||
func _category_style(category: String) -> StyleBoxFlat:
|
||
var style := StyleBoxFlat.new()
|
||
style.set_corner_radius_all(6)
|
||
style.content_margin_left = 7
|
||
style.content_margin_right = 7
|
||
style.content_margin_top = 3
|
||
style.content_margin_bottom = 3
|
||
match category:
|
||
"活动": style.bg_color = Color("55bd8a")
|
||
"招募": style.bg_color = Color("f0aa4c")
|
||
_: style.bg_color = ACCENT_COLOR
|
||
return style
|
||
|
||
func _publish() -> void:
|
||
if _publish_pending:
|
||
return
|
||
var content := _composer.text.strip_edges()
|
||
if content.is_empty():
|
||
return
|
||
var manager := get_node_or_null("/root/ChatManager")
|
||
if manager == null or not manager.has_method("send_world_bulletin"):
|
||
return
|
||
if not bool(manager.call("send_world_bulletin", content)):
|
||
return
|
||
_set_publish_pending(true)
|
||
_set_expanded(true)
|
||
|
||
func _surface_style() -> StyleBoxFlat:
|
||
var style := StyleBoxFlat.new()
|
||
style.bg_color = SURFACE_COLOR
|
||
style.border_color = Color("b8d9f4")
|
||
style.set_border_width_all(2)
|
||
style.set_corner_radius_all(18)
|
||
style.shadow_color = Color(0.16, 0.40, 0.65, 0.18)
|
||
style.shadow_size = 10
|
||
style.shadow_offset = Vector2(0, 4)
|
||
style.content_margin_left = 14
|
||
style.content_margin_top = 12
|
||
style.content_margin_right = 14
|
||
style.content_margin_bottom = 12
|
||
return style
|
||
|
||
func _empty_style() -> StyleBoxEmpty:
|
||
return StyleBoxEmpty.new()
|
||
|
||
func _hover_style() -> StyleBoxFlat:
|
||
var style := StyleBoxFlat.new()
|
||
style.bg_color = Color(0.88, 0.95, 1.0, 0.85)
|
||
style.set_corner_radius_all(12)
|
||
return style
|
||
|
||
func _pressed_style() -> StyleBoxFlat:
|
||
var style := StyleBoxFlat.new()
|
||
style.bg_color = Color(0.78, 0.91, 1.0, 0.9)
|
||
style.set_corner_radius_all(12)
|
||
return style
|
||
|
||
func _input_style() -> StyleBoxFlat:
|
||
var style := StyleBoxFlat.new()
|
||
style.bg_color = Color(1, 1, 1, 0.9)
|
||
style.border_color = Color("c4def4")
|
||
style.set_border_width_all(1)
|
||
style.set_corner_radius_all(12)
|
||
style.content_margin_left = 10
|
||
style.content_margin_right = 10
|
||
return style
|
||
|
||
func _input_focus_style() -> StyleBoxFlat:
|
||
var style := _input_style()
|
||
style.border_color = ACCENT_COLOR
|
||
style.set_border_width_all(2)
|
||
return style
|
||
|
||
func _accent_style() -> StyleBoxFlat:
|
||
var style := StyleBoxFlat.new()
|
||
style.bg_color = ACCENT_COLOR
|
||
style.set_corner_radius_all(14)
|
||
return style
|
||
|
||
func _accent_hover_style() -> StyleBoxFlat:
|
||
var style := _accent_style()
|
||
style.bg_color = Color("53a8f4")
|
||
return style
|
||
|
||
func _accent_pressed_style() -> StyleBoxFlat:
|
||
var style := _accent_style()
|
||
style.bg_color = Color("247bc7")
|
||
return style
|