feat: complete mobile UI implementation
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
# 设置持久化到 `user://system_option.cfg`(= 原 systemSetting 的配置文件)。
|
||||
extends Node
|
||||
|
||||
const MobileTouchButton = preload("res://ui/mobile/mobile_touch_button.gd")
|
||||
const CFG_PATH := "user://system_option.cfg"
|
||||
const CAMERA_MAX := [11.0, 20.0] # 近 / 远(game_camera.max_dist)
|
||||
const FOG_DENSITY := [0.055, 0.018, 0.004] # 浓 / 中 / 淡(Environment.fog_density)
|
||||
@@ -37,6 +38,8 @@ var _env_get: Callable = Callable()
|
||||
|
||||
var _win: Dictionary = {}
|
||||
var _cfg := ConfigFile.new()
|
||||
var _mobile_mode := false
|
||||
var _mobile_root: Control
|
||||
|
||||
# --- 状态(= systemSetting 值)---
|
||||
var music_volume := 0.6
|
||||
@@ -72,18 +75,36 @@ func setup(ui_manager: CanvasLayer, assets: String, audio_node: Node = null,
|
||||
# --- open / close ---------------------------------------------------
|
||||
|
||||
func is_open() -> bool:
|
||||
if _mobile_mode:
|
||||
return _mobile_root != null and is_instance_valid(_mobile_root)
|
||||
return not _win.is_empty() and is_instance_valid(_win.get("root"))
|
||||
|
||||
func set_mobile_mode(enabled: bool) -> void:
|
||||
if not enabled and _mobile_root != null:
|
||||
var old_mode := _mobile_mode
|
||||
_mobile_mode = true
|
||||
close()
|
||||
_mobile_mode = old_mode
|
||||
_mobile_mode = enabled
|
||||
|
||||
func toggle() -> void:
|
||||
if is_open(): close()
|
||||
else: open()
|
||||
|
||||
func close() -> void:
|
||||
if _mobile_mode:
|
||||
if _mobile_root and is_instance_valid(_mobile_root) and ui:
|
||||
ui.close(_mobile_root)
|
||||
_mobile_root = null
|
||||
return
|
||||
if is_open():
|
||||
ui.close(_win["root"])
|
||||
_win = {}
|
||||
|
||||
func open() -> void:
|
||||
if _mobile_mode:
|
||||
_open_mobile()
|
||||
return
|
||||
if is_open():
|
||||
return
|
||||
var path := uiscript_dir.path_join("systemoptiondialog.py")
|
||||
@@ -97,6 +118,91 @@ func open() -> void:
|
||||
_wire()
|
||||
_sync_controls()
|
||||
|
||||
func _open_mobile() -> void:
|
||||
if is_open():
|
||||
return
|
||||
_mobile_root = Control.new()
|
||||
_mobile_root.name = "MobileSystemOptionWindow"
|
||||
_mobile_root.size = Vector2(680, 340)
|
||||
_mobile_root.set_meta("mobile_title", "系统设置")
|
||||
_mobile_root.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
var panel := Panel.new()
|
||||
panel.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
panel.add_theme_stylebox_override("panel", _mobile_panel_style())
|
||||
_mobile_root.add_child(panel)
|
||||
var content := VBoxContainer.new()
|
||||
content.position = Vector2(18, 50)
|
||||
content.size = Vector2(644, 270)
|
||||
content.add_theme_constant_override("separation", 8)
|
||||
_mobile_root.add_child(content)
|
||||
_add_mobile_slider(content, "背景音乐", music_volume, func(v: float): _on_music_volume(v))
|
||||
_add_mobile_slider(content, "音效", sound_volume, func(v: float): _on_sound_volume(v))
|
||||
_add_mobile_radio(content, "镜头距离", ["近", "远"], camera_mode,
|
||||
func(i: int): camera_mode = i; _apply_camera(); _save())
|
||||
_add_mobile_radio(content, "雾", ["浓", "中", "淡"], fog_level,
|
||||
func(i: int): fog_level = i; _apply_fog(); _save())
|
||||
_add_mobile_radio(content, "图形内存", ["CPU", "GPU"], tiling_mode,
|
||||
func(i: int): tiling_mode = i; _save())
|
||||
var hint := Label.new()
|
||||
hint.text = "设置会自动保存;图形内存选项将在下次启动时使用"
|
||||
hint.add_theme_font_size_override("font_size", 11)
|
||||
hint.add_theme_color_override("font_color", Color(0.62, 0.72, 0.84))
|
||||
content.add_child(hint)
|
||||
ui.open(_mobile_root)
|
||||
|
||||
func _add_mobile_slider(parent: VBoxContainer, caption: String, initial: float, changed: Callable) -> void:
|
||||
var row := HBoxContainer.new()
|
||||
row.custom_minimum_size = Vector2(640, 42)
|
||||
var label := Label.new()
|
||||
label.text = caption
|
||||
label.custom_minimum_size = Vector2(92, 38)
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.add_theme_font_size_override("font_size", 13)
|
||||
row.add_child(label)
|
||||
var slider := HSlider.new()
|
||||
slider.min_value = 0.0
|
||||
slider.max_value = 1.0
|
||||
slider.step = 0.01
|
||||
slider.value = initial
|
||||
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
slider.custom_minimum_size = Vector2(500, 38)
|
||||
slider.value_changed.connect(changed)
|
||||
row.add_child(slider)
|
||||
parent.add_child(row)
|
||||
|
||||
func _add_mobile_radio(parent: VBoxContainer, caption: String, choices: Array, active: int, picked: Callable) -> void:
|
||||
var row := HBoxContainer.new()
|
||||
row.custom_minimum_size = Vector2(640, 44)
|
||||
var label := Label.new()
|
||||
label.text = caption
|
||||
label.custom_minimum_size = Vector2(92, 40)
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.add_theme_font_size_override("font_size", 13)
|
||||
row.add_child(label)
|
||||
var buttons: Array[Button] = []
|
||||
for i in choices.size():
|
||||
var button := Button.new()
|
||||
button.text = String(choices[i])
|
||||
button.toggle_mode = true
|
||||
button.button_pressed = i == active
|
||||
button.custom_minimum_size = Vector2(100, 40)
|
||||
var idx := i
|
||||
button.pressed.connect(func():
|
||||
for other in buttons.size():
|
||||
buttons[other].set_pressed_no_signal(other == idx)
|
||||
picked.call(idx))
|
||||
buttons.append(button)
|
||||
row.add_child(button)
|
||||
parent.add_child(row)
|
||||
|
||||
func _mobile_panel_style() -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.04, 0.065, 0.1, 0.98)
|
||||
style.border_color = Color(0.52, 0.68, 0.9, 0.86)
|
||||
style.set_border_width_all(1)
|
||||
style.set_corner_radius_all(8)
|
||||
return style
|
||||
|
||||
func _node(nm: String) -> Control:
|
||||
if _win.is_empty():
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user