feat: complete mobile UI implementation

This commit is contained in:
shenlei
2026-09-04 19:54:33 +09:00
parent 9a4a044da5
commit b19d5b5dd3
132 changed files with 6643 additions and 180 deletions
+154
View File
@@ -0,0 +1,154 @@
# MobileMenuDrawer —— compact two-level menu for the top-right menu button.
#
# The drawer only routes actions. Feature nodes remain the owners of data,
# server state and permission checks.
extends Control
const TouchButton := preload("res://ui/mobile/mobile_touch_button.gd")
signal item_selected(category: String, item: String)
signal opened
signal closed
const CATEGORIES := ["社交", "成长", "活动", "商业", "系统"]
const ITEMS := {
"社交": ["聊天", "好友", "公会", "情侣", "观战"],
"成长": ["精炼", "龙魂", "Cube 制作"],
"活动": ["副本", "钓鱼"],
"商业": ["商店", "商城", "交易", "私人商店", "仓库", "兑换"],
"系统": ["系统设置", "游戏设置", "帮助", "选择角色", "登出", "退出游戏"],
}
var _backdrop: ColorRect
var _panel: Panel
var _category_row: HBoxContainer
var _item_scroll: ScrollContainer
var _items: VBoxContainer
var _title: Label
var _hint: Label
var _active_category := "社交"
func setup() -> void:
set_anchors_preset(Control.PRESET_FULL_RECT)
mouse_filter = Control.MOUSE_FILTER_IGNORE
_build()
visible = false
func is_open() -> bool:
return visible
func toggle() -> void:
if visible:
close()
else:
open()
func open() -> void:
if visible:
return
visible = true
_refresh_items()
opened.emit()
func close() -> void:
if not visible:
return
visible = false
closed.emit()
func _build() -> void:
_backdrop = ColorRect.new()
_backdrop.color = Color(0.01, 0.02, 0.035, 0.38)
_backdrop.set_anchors_preset(Control.PRESET_FULL_RECT)
_backdrop.mouse_filter = Control.MOUSE_FILTER_STOP
_backdrop.gui_input.connect(func(event: InputEvent):
if event is InputEventScreenTouch and not event.pressed:
close()
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
close())
add_child(_backdrop)
_panel = Panel.new()
_panel.set_anchors_preset(Control.PRESET_TOP_RIGHT)
_panel.position = Vector2(-366, 72)
_panel.size = Vector2(350, 300)
_panel.mouse_filter = Control.MOUSE_FILTER_STOP
_panel.add_theme_stylebox_override("panel", _panel_style())
add_child(_panel)
_title = Label.new()
_title.position = Vector2(18, 13)
_title.add_theme_font_size_override("font_size", 18)
_title.add_theme_color_override("font_color", Color(0.95, 0.82, 0.52))
_panel.add_child(_title)
var close_button := TouchButton.new()
close_button.setup("×", Color(0.6, 0.75, 0.96, 0.9))
close_button.set_rect_style()
close_button.position = Vector2(310, 7)
close_button.size = Vector2(32, 32)
close_button.custom_minimum_size = Vector2(32, 32)
close_button.pressed.connect(close)
_panel.add_child(close_button)
_category_row = HBoxContainer.new()
_category_row.position = Vector2(14, 48)
_category_row.size = Vector2(322, 38)
_category_row.add_theme_constant_override("separation", 4)
_panel.add_child(_category_row)
for category in CATEGORIES:
var button := TouchButton.new()
button.setup(category, Color(0.45, 0.7, 0.95, 0.9))
button.set_rect_style()
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button.custom_minimum_size = Vector2(0, 36)
var selected: String = String(category)
button.pressed.connect(func():
_active_category = selected
_refresh_items())
_category_row.add_child(button)
_item_scroll = ScrollContainer.new()
_item_scroll.position = Vector2(16, 96)
_item_scroll.size = Vector2(318, 160)
_item_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
_item_scroll.mouse_filter = Control.MOUSE_FILTER_STOP
_panel.add_child(_item_scroll)
_items = VBoxContainer.new()
_items.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_items.add_theme_constant_override("separation", 5)
_item_scroll.add_child(_items)
_hint = Label.new()
_hint.position = Vector2(18, 264)
_hint.size = Vector2(314, 26)
_hint.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_hint.add_theme_font_size_override("font_size", 11)
_hint.add_theme_color_override("font_color", Color(0.66, 0.72, 0.8))
_panel.add_child(_hint)
func _refresh_items() -> void:
if _panel == null:
return
_title.text = _active_category
_hint.text = "功能入口仍受当前场景和服务器状态限制。"
for child in _items.get_children():
child.queue_free()
for item in ITEMS.get(_active_category, []):
var button := TouchButton.new()
button.setup(" " + String(item), Color(0.42, 0.65, 0.9, 0.8))
button.set_rect_style()
button.custom_minimum_size = Vector2(0, 34)
var selected: String = String(item)
button.pressed.connect(func(): item_selected.emit(_active_category, selected))
_items.add_child(button)
func _panel_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.045, 0.065, 0.1, 0.96)
style.border_color = Color(0.52, 0.68, 0.9, 0.82)
style.set_border_width_all(1)
style.set_corner_radius_all(12)
style.shadow_color = Color(0, 0, 0, 0.45)
style.shadow_size = 12
return style