# 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