155 lines
4.8 KiB
GDScript
155 lines
4.8 KiB
GDScript
# 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
|