From cfeb924b2f13b04db93bbfedbd36462e547d4ccd Mon Sep 17 00:00:00 2001 From: wusumac <736139669@qq.com> Date: Mon, 14 Sep 2026 02:52:00 +0800 Subject: [PATCH] feat(m63-d): migrate Settings onto the shared chrome --- runtime/entry/entry_shell.gd | 119 +++++++++++++++++------- runtime/tools/m63_entry_visual_probe.gd | 38 +++++++- 2 files changed, 123 insertions(+), 34 deletions(-) diff --git a/runtime/entry/entry_shell.gd b/runtime/entry/entry_shell.gd index 8f90abb9..07c8bb51 100644 --- a/runtime/entry/entry_shell.gd +++ b/runtime/entry/entry_shell.gd @@ -7194,18 +7194,38 @@ func show_settings(category := "") -> void: safe.add_theme_constant_override("margin_top", 12 if short_layout else 24) safe.add_theme_constant_override("margin_bottom", 12 if short_layout else 24) add_child(safe) - safe.add_child(_entry_chrome("SettingsFrame", "settings_frame")) + var settings_frame := PageChrome.new().configure("card") + settings_frame.name = "SettingsFrame" + settings_frame.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + safe.add_child(settings_frame) + # The full-page parchment card rolls 5.5% horizontally and 15% vertically; + # everything must live on the face between those edges. + var card_width := maxf(0.0, size.x - float(margin) * 2.0) + var card_height := maxf(0.0, size.y - (12.0 if short_layout else 24.0) * 2.0) + var card_inset_x := card_width * PageChrome.PANEL_EDGE_X + 8.0 + var card_inset_y := card_height * PageChrome.PANEL_EDGE_Y + 8.0 + var face := MarginContainer.new() + face.name = "SettingsFace" + face.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + face.add_theme_constant_override("margin_left", int(card_inset_x)) + face.add_theme_constant_override("margin_right", int(card_inset_x)) + face.add_theme_constant_override("margin_top", int(card_inset_y)) + face.add_theme_constant_override("margin_bottom", int(card_inset_y)) + safe.add_child(face) var outer := VBoxContainer.new() outer.name = "SettingsOuter" outer.add_theme_constant_override("separation", 8) - safe.add_child(outer) + face.add_child(outer) var heading := HBoxContainer.new() heading.name = "SettingsHeading" outer.add_child(heading) - var title := _label(_tr("设置", "SETTINGS"), _scaled(24 if short_layout else 26), Color("#f5ead0")) + var title := _label(_tr("设置", "SETTINGS"), UITokens.font_size("heading", _entry_text_scale()), PageChrome.INK) + title.name = "SettingsTitle" title.size_flags_horizontal = Control.SIZE_EXPAND_FILL heading.add_child(title) - heading.add_child(_label(_tr("来源:标题首页", "Caller: Title"), _scaled(12), Color("#e8d39d"))) + var caller_label := _label(_tr("来源:标题首页", "Caller: Title"), UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK_SOFT) + caller_label.name = "SettingsCaller" + heading.add_child(caller_label) var workspace := HBoxContainer.new() workspace.name = "SettingsWorkspace" workspace.size_flags_vertical = Control.SIZE_EXPAND_FILL @@ -7249,7 +7269,10 @@ func show_settings(category := "") -> void: workspace.add_child(scroll) var content := VBoxContainer.new() content.name = "SettingsContent" - content.size_flags_horizontal = Control.SIZE_EXPAND_FILL + # A 20:1 shared plate would show metre-wide rolled ends; cap the reading + # column so plates stay in the authored aspect range. + content.custom_minimum_size.x = 720.0 + content.size_flags_horizontal = Control.SIZE_SHRINK_CENTER content.add_theme_constant_override("separation", 6) scroll.add_child(content) _build_settings_category(content) @@ -7257,7 +7280,7 @@ func show_settings(category := "") -> void: footer.name = "SettingsFooter" footer.add_theme_constant_override("separation", 8) outer.add_child(footer) - var notice := _label(settings_notice, _scaled(12 if short_layout else 13), Color("#f0ca75")) + var notice := _label(settings_notice, UITokens.font_size("micro", _entry_text_scale()), UITokens.COLOR_DANGER) notice.name = "SettingsNotice" notice.size_flags_horizontal = Control.SIZE_EXPAND_FILL footer.add_child(notice) @@ -7301,17 +7324,17 @@ func _build_settings_category(content: VBoxContainer) -> void: content.add_child(_choice_button("SettingDynamicRange", _tr("动态范围", "Dynamic range"), String(settings_draft.dynamic_range).capitalize(), cycle_dynamic_range)) "controls": content.add_child(_settings_heading(_tr("控制", "CONTROLS"))) - content.add_child(_label(_tr("触控、键鼠与控制器共享确认、取消和导航语义。", "Touch, keyboard/mouse and controller share confirm, cancel and navigation semantics."), _scaled(15), Color("#aebbc2"))) + content.add_child(_label(_tr("触控、键鼠与控制器共享确认、取消和导航语义。", "Touch, keyboard/mouse and controller share confirm, cancel and navigation semantics."), UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK_SOFT)) content.add_child(_slider_row("SettingHoldDuration", _tr("长按确认时间", "Hold duration"), float(settings_draft.hold_duration), 0.3, 1.5, 0.1, set_hold_duration)) content.add_child(_check_button("SettingVibration", _tr("震动", "Vibration"), bool(settings_draft.vibration), set_vibration)) content.add_child(_unavailable_row("SettingRemapUnavailable", _tr("按键重映射:DEV-1 仅提供默认入口映射;完整冲突检查属于后续控制页面", "Remapping: DEV-1 uses the default entry map; conflict-safe remapping belongs to the later Controls page"))) "language": content.add_child(_settings_heading(_tr("语言与地区格式", "LANGUAGE & LOCALE"))) content.add_child(_choice_button("SettingLocale", _tr("界面语言", "Interface language"), _tr("简体中文", "English"), cycle_setting_locale)) - content.add_child(_label(_tr("日期 2026-08-17 · 数字 1,234.5 · 文本可换行", "Date 2026-08-17 · Number 1,234.5 · Text may wrap"), _scaled(16), Color("#f5ead0"))) + content.add_child(_label(_tr("日期 2026-08-17 · 数字 1,234.5 · 文本可换行", "Date 2026-08-17 · Number 1,234.5 · Text may wrap"), UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK)) "credits": content.add_child(_settings_heading(_tr("制作人员、许可与数据", "CREDITS, LICENSES & DATA"))) - var credits := _label(_credits_text(), _scaled(14), Color("#f5ead0")) + var credits := _label(_credits_text(), UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK) credits.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART credits.size_flags_horizontal = Control.SIZE_EXPAND_FILL content.add_child(credits) @@ -7462,14 +7485,14 @@ func _next_float(options: Array, current: float) -> float: func _settings_heading(text_value: String) -> Label: - return _label(text_value, _scaled(19), Color("#e8d39d")) + return _label(text_value, UITokens.font_size("label", _entry_text_scale()), PageChrome.INK) func _slider_row(node_name: String, text_value: String, value: float, minimum: float, maximum: float, step: float, callback: Callable) -> HBoxContainer: var row := HBoxContainer.new() row.name = node_name row.custom_minimum_size.y = 42 - var label := _label("%s %.1f" % [text_value, value], _scaled(14), Color("#f5ead0")) + var label := _label("%s %.1f" % [text_value, value], UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK) label.custom_minimum_size.x = 190 row.add_child(label) var slider := HSlider.new() @@ -7480,9 +7503,10 @@ func _slider_row(node_name: String, text_value: String, value: float, minimum: f slider.value = value slider.custom_minimum_size.y = 24 slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL - slider.add_theme_stylebox_override("slider", _flat_style(Color("#0d151d"), Color("#866f47"), 1, 3)) - slider.add_theme_stylebox_override("grabber_area", _flat_style(Color("#73b9e7"), Color("#ead49b"), 1, 3)) - slider.add_theme_stylebox_override("grabber_area_highlight", _flat_style(Color("#a9d9f2"), Color("#f5ead0"), 1, 3)) + # No shared slider component exists yet; the track keeps token colours. + slider.add_theme_stylebox_override("slider", _flat_style(Color("#0d151d"), Color("#a8935f"), 1, 3)) + slider.add_theme_stylebox_override("grabber_area", _flat_style(Color("#397681"), PageChrome.INK_SOFT, 1, 3)) + slider.add_theme_stylebox_override("grabber_area_highlight", _flat_style(Color("#4f9fb0"), PageChrome.INK, 1, 3)) slider.value_changed.connect(callback) row.add_child(slider) return row @@ -7753,16 +7777,18 @@ func _choice_button(node_name: String, label_text: String, value_text: String, c button.tooltip_text = "%s: %s" % [label_text, value_text] button.custom_minimum_size = Vector2(0, 48) button.size_flags_horizontal = Control.SIZE_EXPAND_FILL - var label := _button_label("ChoiceLabel", label_text, _scaled(15), Color("#f5ead0")) + var label := _button_label("ChoiceLabel", label_text, UITokens.font_size("label", _entry_text_scale()), PageChrome.INK) + label.anchor_left = PageChrome.PLATE_EDGE_X label.anchor_right = 0.58 - label.offset_left = 14.0 + label.offset_left = 6.0 label.offset_right = -6.0 label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT button.add_child(label) - var value := _button_label("ValueLabel", value_text, _scaled(14), Color("#202a31")) + var value := _button_label("ValueLabel", value_text, UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK) value.anchor_left = 0.58 + value.anchor_right = 1.0 - PageChrome.PLATE_EDGE_X value.offset_left = 6.0 - value.offset_right = -10.0 + value.offset_right = -6.0 button.add_child(value) return button @@ -7771,9 +7797,11 @@ func _action_button(node_name: String, text_value: String, callback: Callable) - var button := _entry_button_base(node_name, callback, "action_primary") button.tooltip_text = text_value button.custom_minimum_size = Vector2(UITokens.command_width("primary"), UITokens.command_height("primary")) - var label := _button_label("ButtonLabel", text_value, _scaled(16), Color("#f5ead0")) - label.offset_left = 46.0 - label.offset_right = -10.0 + var label := _button_label("ButtonLabel", text_value, UITokens.font_size("label", _entry_text_scale()), PageChrome.INK) + label.anchor_left = PageChrome.PLATE_EDGE_X + label.anchor_right = 1.0 - PageChrome.PLATE_EDGE_X + label.offset_left = 6.0 + label.offset_right = -6.0 button.add_child(label) return button @@ -7782,9 +7810,11 @@ func _tool_button(node_name: String, text_value: String, callback: Callable) -> var button := _entry_button_base(node_name, callback, "tool") button.tooltip_text = text_value button.custom_minimum_size = Vector2(UITokens.command_width("secondary"), UITokens.command_height("secondary")) - var label := _button_label("ButtonLabel", text_value, _scaled(14), Color("#f5ead0")) - label.offset_left = 16.0 - label.offset_right = -10.0 + var label := _button_label("ButtonLabel", text_value, UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK) + label.anchor_left = PageChrome.PLATE_EDGE_X + label.anchor_right = 1.0 - PageChrome.PLATE_EDGE_X + label.offset_left = 6.0 + label.offset_right = -6.0 button.add_child(label) return button @@ -7798,9 +7828,28 @@ func _check_button(node_name: String, text_value: String, enabled: bool, callbac check.flat = true for style_name in ["normal", "hover", "pressed", "disabled", "focus", "hover_pressed"]: check.add_theme_stylebox_override(style_name, StyleBoxEmpty.new()) - check.add_child(_entry_chrome("Chrome", "toggle")) - var label := _button_label("ButtonLabel", text_value, _scaled(14), Color("#f5ead0")) - label.offset_left = 14.0 + # The switch state is a shared pip so on/off reads without the old + # bespoke toggle art. + var chrome := PageChrome.new().configure("command_plate", {"state": "secondary"}) + chrome.name = "Chrome" + chrome.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + check.add_child(chrome) + var switch_pip := PageChrome.new().configure("pip", {"shape": "diamond", "filled": enabled}) + switch_pip.name = "SwitchPip" + switch_pip.anchor_left = 1.0 + switch_pip.anchor_right = 1.0 + switch_pip.anchor_top = 0.5 + switch_pip.anchor_bottom = 0.5 + switch_pip.offset_left = -54.0 + switch_pip.offset_right = -22.0 + switch_pip.offset_top = -14.0 + switch_pip.offset_bottom = 14.0 + check.add_child(switch_pip) + check.toggled.connect(func(on: bool) -> void: switch_pip.configure("pip", {"shape": "diamond", "filled": on}).queue_redraw()) + var label := _button_label("ButtonLabel", text_value, UITokens.font_size("micro", _entry_text_scale()), PageChrome.INK) + label.anchor_left = PageChrome.PLATE_EDGE_X + label.anchor_right = 1.0 - PageChrome.PLATE_EDGE_X + label.offset_left = 6.0 label.offset_right = -52.0 label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART @@ -7815,7 +7864,13 @@ func _entry_button_base(node_name: String, callback: Callable, variant: String) button.flat = true for style_name in ["normal", "hover", "pressed", "disabled", "focus", "hover_pressed"]: button.add_theme_stylebox_override(style_name, StyleBoxEmpty.new()) - button.add_child(_entry_chrome("Chrome", variant)) + # M63-D: entry-family buttons use the shared hand-drawn plate; the authored + # `Chrome` node name and variant meta stay for the focused contract. + var plate_state := "primary" if variant == "action_primary" else "secondary" + var chrome := PageChrome.new().configure("command_plate", {"state": plate_state}) + chrome.name = "Chrome" + chrome.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + button.add_child(chrome) button.set_meta("entry_chrome_variant", variant) button.pressed.connect(callback, CONNECT_DEFERRED) return button @@ -7839,13 +7894,11 @@ func _set_entry_button_disabled(button: Button, disabled: bool, selected := fals if label == null: continue if selected: - label.add_theme_color_override("font_color", Color("#202a31")) + label.add_theme_color_override("font_color", PageChrome.INK) elif disabled: - label.add_theme_color_override("font_color", Color("#4f565b") if child_name == "ValueLabel" else Color("#89939a")) - elif child_name == "ValueLabel": - label.add_theme_color_override("font_color", Color("#202a31")) + label.add_theme_color_override("font_color", UITokens.COLOR_MUTED) else: - label.add_theme_color_override("font_color", Color("#f5ead0")) + label.add_theme_color_override("font_color", PageChrome.INK) func _set_entry_button_text(button: Button, text_value: String) -> void: diff --git a/runtime/tools/m63_entry_visual_probe.gd b/runtime/tools/m63_entry_visual_probe.gd index a669dc91..3cf4aad0 100644 --- a/runtime/tools/m63_entry_visual_probe.gd +++ b/runtime/tools/m63_entry_visual_probe.gd @@ -52,6 +52,9 @@ func _run() -> void: await _capture_setup(size, locale) await _capture_title(size, locale, "fresh") await _capture_title(size, locale, "recoverable") + if size == AUTHORITY_SIZE: + await _capture_settings(size, locale, "display") + await _capture_settings(size, locale, "audio") var report_path := output_root.path_join("ink-violations-m63-entry.json") var file := FileAccess.open(report_path, FileAccess.WRITE) if file != null: @@ -67,7 +70,7 @@ func _run() -> void: return if title_recoverable_ok: print("ABG_M63_TITLE_RECOVERABLE_OK new_game=enabled continue=enabled") - print("ABG_M63_ENTRY_VISUAL_PROBE_OK sizes=2 locales=2 pages=boot,setup,title states=running,error,fresh,recoverable views=static,dynamic,final") + print("ABG_M63_ENTRY_VISUAL_PROBE_OK sizes=2 locales=2 pages=boot,setup,title,settings states=running,error,fresh,recoverable,display,audio views=static,dynamic,final") quit(0) @@ -231,6 +234,39 @@ func _capture_title(size: Vector2i, locale: String, state: String) -> void: await process_frame +func _capture_settings(size: Vector2i, locale: String, category: String) -> void: + var viewport := SubViewport.new() + viewport.size = size + viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS + root.add_child(viewport) + var shell := EntryShell.new() + shell.auto_start = false + viewport.add_child(shell) + await _frames(3) + shell.settings = SettingsStore.new().defaults() + shell.settings.setup_completed = true + shell.settings.locale = locale + shell.size = Vector2(size) + shell.show_settings(category) + await _frames(4) + _scan_ink(shell, locale, "settings-%s-%dx%d" % [category, size.x, size.y], true) + await _save(viewport, "settings-%s-%dx%d-%s" % [category, size.x, size.y, locale], "final") + for node_name in ["SettingsTitle", "SettingsCaller", "SettingsNotice"]: + var control := shell.find_child(node_name, true, false) as CanvasItem + if control != null: + control.visible = false + var content := shell.find_child("SettingsContent", true, false) as Control + if content != null: + for child in content.get_children(): + var canvas := child as CanvasItem + if canvas != null: + canvas.visible = false + await _frames(2) + await _save(viewport, "settings-%s-%dx%d-%s" % [category, size.x, size.y, locale], "static") + viewport.queue_free() + await process_frame + + func _scan_ink(view: Control, locale: String, label: String, strict := true) -> void: var violations := InkCheck.scan(view) if not strict: