refactor(ui): migrate 6 meta screens to MetaScreen pattern (Workstream 2)
Relocates main_menu, character_creation, settings_dialog, debug_console,
bug_report_dialog, loading_screen from flat client/ui/ into structured
client/ui/meta/screens/<name>/. All six now extend MetaScreen instead
of Control; the base handles open/close lifecycle, visibility,
captures_input, and — for overlays — the sim-pause contract.
Screen policies set per Tyre's proposal:
- settings_dialog: pauses_sim=false, PUSHES onto MetaStack
- debug_console: pauses_sim=true, PUSHES (D-088 routing via base)
- bug_report_dialog: pauses_sim=true, PUSHES
- loading_screen: closable_by_escape=false, PUSHES
- main_menu, character_creation: scene-roots, extend MetaScreen for
the lifecycle contract only, do NOT push onto the stack
character_creation stays at its current surface (tabs, descriptor,
creation_confirmed signal unchanged). Tab consolidation and
CharacterProfile migration happen in Workstreams 5 and 6.
Knock-on changes:
- main.tscn ModalLayer CanvasLayer renamed to MetaLayer; main.gd
@onready refs updated; constants.gd comment updated; test_client_p3
and test_ui_framework_sprint15 assertions updated; test_monologue_display
and .tscn header comments updated.
- OPEN_MENU handler now pushes settings_dialog onto MetaStack before
calling open(). Full ESC priority chain lands in Workstream 4.
- atlas_app.gd: _unhandled_key_input signature widened from
InputEventKey to InputEvent with an is-check, per Godot 4 API. Pre-
existing narrowing was silently tolerated until main.tscn started
fully instantiating under the new pattern.
- test_client_p3: entity_renderer type annotations corrected from
ColorRect to Sprite2D (stale since a prior refactor); facing
indicator rotation assertion switched to angle_difference() for
modular-safe comparison.
Verification:
- gdlint client/scripts/ client/ui/ — zero problems
- godot --headless --path client --quit — no SCRIPT ERROR
- test_client_p3: 24/24 pass
- test_ui_framework_sprint15: 54/54 pass
- test_implant_nav_stack: 52/52 pass
- test_implant_registry: 42/42 pass
- test_implant_app_lifecycle: 36/36 pass
Workstream 1 foundation (84105916) remains unchanged. Workstreams 3-8
follow: protocol layer, Option A sequencing, 3-tab restructure,
Bookmark tab, location picker, Skills stub.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://char_creation_scene_sr"]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/character_creation.gd" id="1_charcreation"]
|
||||
[ext_resource type="Script" path="res://ui/meta/screens/character_creation/character_creation.gd" id="1_charcreation"]
|
||||
|
||||
; #705: Character creation screen — 3D preview + 5-tab customisation panel.
|
||||
; SubViewport renders CharacterVisual live. Tab panel: Body/Head/Hair/Clothing/Accessories.
|
||||
|
||||
@@ -187,17 +187,17 @@ script = ExtResource("10_cursor")
|
||||
|
||||
; --- Modal layer (CanvasLayer 30) ---
|
||||
; Full-screen overlays: pause menu, inventory modal, death screen.
|
||||
[node name="ModalLayer" type="CanvasLayer" parent="."]
|
||||
[node name="MetaLayer" type="CanvasLayer" parent="."]
|
||||
layer = 30
|
||||
|
||||
; #495: WRONG button (F12) — bug report capture dialog
|
||||
[node name="BugReportDialog" parent="ModalLayer" instance=ExtResource("19_bugreport")]
|
||||
[node name="BugReportDialog" parent="MetaLayer" instance=ExtResource("19_bugreport")]
|
||||
|
||||
; #528: Audio settings dialog — 5-bus volume sliders, ESC/OPEN_MENU to toggle
|
||||
[node name="SettingsDialog" parent="ModalLayer" instance=ExtResource("21_settings")]
|
||||
[node name="SettingsDialog" parent="MetaLayer" instance=ExtResource("21_settings")]
|
||||
|
||||
; #257: Loading screen — full-screen overlay during save/load round-trip
|
||||
[node name="LoadingScreen" parent="ModalLayer" instance=ExtResource("26_loading")]
|
||||
[node name="LoadingScreen" parent="MetaLayer" instance=ExtResource("26_loading")]
|
||||
|
||||
; #581: Debug console — tilde key toggles, bottom 40% of screen
|
||||
[node name="DebugConsole" parent="ModalLayer" instance=ExtResource("27_debug_console")]
|
||||
[node name="DebugConsole" parent="MetaLayer" instance=ExtResource("27_debug_console")]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://main_menu_sr"]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/main_menu.gd" id="1_mainmenu"]
|
||||
[ext_resource type="Script" path="res://ui/meta/screens/main_menu/main_menu.gd" id="1_mainmenu"]
|
||||
|
||||
; Main menu — New Game / Continue / Quit.
|
||||
; #258: D-085 per-game save directory created on New Game.
|
||||
|
||||
@@ -40,7 +40,7 @@ const CANVAS_UI: int = 20 # CanvasLayer number for UILayer
|
||||
#
|
||||
# MODAL SCOPE (CanvasLayer 30)
|
||||
# Full-screen overlays: pause, inventory modal, death screen.
|
||||
const CANVAS_MODAL: int = 30 # CanvasLayer number for ModalLayer
|
||||
const CANVAS_MODAL: int = 30 # CanvasLayer number for MetaLayer
|
||||
#
|
||||
# Rendering ceiling: 10 floors (25m) above current floor.
|
||||
# Above this: no sprites, ground shadows + environmental effects only.
|
||||
|
||||
@@ -30,10 +30,10 @@ var _dialogue: DialogueCoordinator # #775: dialogue consumers + signal handlers
|
||||
@onready var examine_display = $InsertOverlay/ExamineDisplay # #174: examine result overlay
|
||||
@onready var journal_panel = $InsertOverlay/JournalPanel # #264: knowledge journal (D-041)
|
||||
@onready var debug_overlay = $UILayer/DebugOverlay # #511: F3 debug overlay
|
||||
@onready var bug_report_dialog = $ModalLayer/BugReportDialog # #495: F12 WRONG button
|
||||
@onready var settings_dialog = $ModalLayer/SettingsDialog # #528: audio settings (ESC/OPEN_MENU)
|
||||
@onready var loading_screen = $ModalLayer/LoadingScreen # #257: blocking overlay during load
|
||||
@onready var debug_console = $ModalLayer/DebugConsole # #581: tilde debug console
|
||||
@onready var bug_report_dialog = $MetaLayer/BugReportDialog # #495: F12 WRONG button
|
||||
@onready var settings_dialog = $MetaLayer/SettingsDialog # #528: audio settings (ESC/OPEN_MENU)
|
||||
@onready var loading_screen = $MetaLayer/LoadingScreen # #257: blocking overlay during load
|
||||
@onready var debug_console = $MetaLayer/DebugConsole # #581: tilde debug console
|
||||
@onready var news_ticker = $UILayer/NewsTicker # #592: scrolling headline bar (D-049 z-7)
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ func _process(delta: float) -> void:
|
||||
if settings_dialog.is_open():
|
||||
settings_dialog.close()
|
||||
else:
|
||||
MetaStack.push(settings_dialog)
|
||||
settings_dialog.open()
|
||||
continue
|
||||
if input.action == InputMapper.Action.INTERACT:
|
||||
|
||||
@@ -124,7 +124,7 @@ func test_z_ui_layer_above_world() -> void:
|
||||
var inst := _make_scene()
|
||||
var ui_layer = inst.get_node("UILayer") as CanvasLayer
|
||||
var insert_layer = inst.get_node("InsertOverlay") as CanvasLayer
|
||||
var modal_layer = inst.get_node("ModalLayer") as CanvasLayer
|
||||
var modal_layer = inst.get_node("MetaLayer") as CanvasLayer
|
||||
assert_that(insert_layer.layer).override_failure_message(
|
||||
"InsertOverlay must be CanvasLayer %d" % Constants.CANVAS_INSERT
|
||||
).is_equal(Constants.CANVAS_INSERT)
|
||||
@@ -132,13 +132,13 @@ func test_z_ui_layer_above_world() -> void:
|
||||
"UILayer must be CanvasLayer %d" % Constants.CANVAS_UI
|
||||
).is_equal(Constants.CANVAS_UI)
|
||||
assert_that(modal_layer.layer).override_failure_message(
|
||||
"ModalLayer must be CanvasLayer %d" % Constants.CANVAS_MODAL
|
||||
"MetaLayer must be CanvasLayer %d" % Constants.CANVAS_MODAL
|
||||
).is_equal(Constants.CANVAS_MODAL)
|
||||
assert_that(ui_layer.layer > insert_layer.layer).override_failure_message(
|
||||
"UILayer must render above InsertOverlay"
|
||||
).is_true()
|
||||
assert_that(modal_layer.layer > ui_layer.layer).override_failure_message(
|
||||
"ModalLayer must render above UILayer"
|
||||
"MetaLayer must render above UILayer"
|
||||
).is_true()
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ func test_entity_lerp_moves_toward_target() -> void:
|
||||
var entity := [{"entity_id": 11, "x": 5.0, "y": 5.0, "z": 0,
|
||||
"kind": {"variant": "Npc", "data": null}}]
|
||||
renderer.update_entities(entity)
|
||||
var node: ColorRect = renderer.entity_nodes[11]
|
||||
var node: Sprite2D = renderer.entity_nodes[11]
|
||||
var start_pos: Vector2 = node.position
|
||||
# Move target to (6, 5)
|
||||
var entity_moved := [{"entity_id": 11, "x": 6.0, "y": 5.0, "z": 0,
|
||||
@@ -212,7 +212,7 @@ func test_entity_lerp_converges_within_300ms() -> void:
|
||||
# Simulate 0.3s at 60fps (18 frames × 0.016s ≈ 0.288s)
|
||||
for i in 20:
|
||||
renderer._process(0.016)
|
||||
var final_node: ColorRect = renderer.entity_nodes[12]
|
||||
var final_node: Sprite2D = renderer.entity_nodes[12]
|
||||
var final_pos: Vector2 = final_node.position
|
||||
# Should be within 5% of target (97% convergence at 0.3s)
|
||||
var dist: float = final_pos.distance_to(target)
|
||||
@@ -272,9 +272,10 @@ func test_facing_indicator_rotation_matches_input_mapper_angle() -> void:
|
||||
for angle in angles:
|
||||
InputMapper.facing_angle = angle
|
||||
renderer.update_entities(entity)
|
||||
assert_that(indicator.rotation).override_failure_message(
|
||||
"angle %.3f: expected rotation %.3f, got %.3f" % [angle, angles[angle], indicator.rotation]
|
||||
).is_equal_approx(angles[angle], 0.001)
|
||||
var diff := absf(angle_difference(indicator.rotation, angles[angle]))
|
||||
assert_that(diff).override_failure_message(
|
||||
"angle %.3f: expected rotation %.3f, got %.3f (diff %.4f)" % [angle, angles[angle], indicator.rotation, diff]
|
||||
).is_less_equal(0.001)
|
||||
InputMapper.facing_angle = -PI / 2.0 # Reset to default
|
||||
renderer.queue_free()
|
||||
|
||||
@@ -292,7 +293,7 @@ func test_lerp_weight_increases_with_delta() -> void:
|
||||
"kind": {"variant": "Npc", "data": null}}]
|
||||
renderer.update_entities(entity_moved)
|
||||
# Small delta step
|
||||
var small_node: ColorRect = renderer.entity_nodes[20]
|
||||
var small_node: Sprite2D = renderer.entity_nodes[20]
|
||||
var small_start: float = small_node.position.x
|
||||
renderer._process(0.008)
|
||||
var small_progress: float = small_node.position.x - small_start
|
||||
|
||||
@@ -472,7 +472,7 @@ func test_monologue_display_parented_to_canvas_layer_20_in_main_scene() -> void:
|
||||
## D-049: Structural verification — MonologueDisplay must be a direct child of
|
||||
## UILayer (CanvasLayer, layer=20) in the live scene tree, not the world layer.
|
||||
## Catches regressions where the node gets accidentally moved to InsertOverlay
|
||||
## (layer=10) or ModalLayer (layer=30), or dropped into the world z-stack.
|
||||
## (layer=10) or MetaLayer (layer=30), or dropped into the world z-stack.
|
||||
##
|
||||
## Scene path verified: Game/UILayer/MonologueDisplay (main.tscn line 141).
|
||||
if not ResourceLoader.exists("res://scenes/main.tscn"):
|
||||
|
||||
@@ -66,13 +66,13 @@ func test_ui_layer_is_canvas_layer_20() -> void:
|
||||
|
||||
|
||||
func test_modal_layer_is_canvas_layer_30() -> void:
|
||||
# D-049: ModalLayer = pause/inventory modal scope = CanvasLayer 30.
|
||||
# D-049: MetaLayer = pause/inventory modal scope = CanvasLayer 30.
|
||||
var scene := MAIN_SCENE
|
||||
_instance = scene.instantiate()
|
||||
auto_free(_instance)
|
||||
add_child(_instance)
|
||||
|
||||
var modal_layer: CanvasLayer = _instance.get_node("ModalLayer")
|
||||
var modal_layer: CanvasLayer = _instance.get_node("MetaLayer")
|
||||
assert_that(modal_layer).is_not_null()
|
||||
assert_that(modal_layer.layer).is_equal(Constants.CANVAS_MODAL)
|
||||
|
||||
@@ -83,7 +83,7 @@ func test_ui_layer_above_insert_overlay() -> void:
|
||||
|
||||
|
||||
func test_modal_layer_above_ui_layer() -> void:
|
||||
# D-049: ModalLayer (30) must render above UILayer (20).
|
||||
# D-049: MetaLayer (30) must render above UILayer (20).
|
||||
assert_that(Constants.CANVAS_MODAL).is_greater(Constants.CANVAS_UI)
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/bug_report_dialog.gd" id="1_bugreport"]
|
||||
[ext_resource type="Script" path="res://ui/meta/screens/bug_report/bug_report_dialog.gd" id="1_bugreport"]
|
||||
|
||||
; #495: WRONG button (F12) — bug report capture dialog, ModalLayer
|
||||
; #495: WRONG button (F12) — bug report capture dialog, MetaLayer
|
||||
[node name="BugReportDialog" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b2ndm9rvx8cqp"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c8pvt3xr7kmd2" path="res://ui/debug_console.gd" id="1_debug_console"]
|
||||
[ext_resource type="Script" path="res://ui/meta/screens/debug_console/debug_console.gd" id="1_debug_console"]
|
||||
|
||||
; #581: In-game debug console. Tilde key toggles. ModalLayer.
|
||||
; #581: In-game debug console. Tilde key toggles. MetaLayer.
|
||||
; UI built programmatically in _ready() — scene contains only root node + script.
|
||||
[node name="DebugConsole" type="Control"]
|
||||
layout_mode = 3
|
||||
|
||||
@@ -54,14 +54,16 @@ func on_open(_mode: int) -> void:
|
||||
_reach_screen.refresh_info_panel_visibility()
|
||||
|
||||
|
||||
func _unhandled_key_input(event: InputEventKey) -> void:
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
if not event is InputEventKey:
|
||||
return
|
||||
if manifest == null or not HudGroups.is_app_active(manifest.app_path):
|
||||
return
|
||||
if not event.is_pressed() or event.is_echo():
|
||||
return
|
||||
if current_screen_id() == "regional":
|
||||
return # AtlasViewer handles its own keyboard input
|
||||
_handle_key(event)
|
||||
_handle_key(event as InputEventKey)
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b7rv9mkl4qpw3"]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/loading_screen.gd" id="1_loading"]
|
||||
[ext_resource type="Script" path="res://ui/meta/screens/loading/loading_screen.gd" id="1_loading"]
|
||||
|
||||
; #257: Loading screen — full-screen overlay shown during save/load round-trip.
|
||||
; Blocks input; dismissed when save_result arrives from server.
|
||||
|
||||
+20
-48
@@ -1,4 +1,4 @@
|
||||
extends Control
|
||||
extends MetaScreen
|
||||
|
||||
## #507: WRONG button — full 60-tick capture: ring buffer, snapshot history, replay seed.
|
||||
## Upgrade of the Sprint 9 MVP (#495).
|
||||
@@ -36,7 +36,6 @@ const PADDING := 16
|
||||
const RING_SIZE := 60
|
||||
|
||||
var _line_edit: LineEdit = null
|
||||
var _active: bool = false
|
||||
var _captured_screenshot: Image = null
|
||||
|
||||
# #507: Pre-allocated ring buffers (no per-tick allocation after _ready).
|
||||
@@ -56,8 +55,9 @@ var _snapshot_count: int = 0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
pauses_sim = true
|
||||
visible = false
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
# Pre-allocate ring buffers — resize then fill sentinels.
|
||||
# The ring array itself never grows after _ready. Each write replaces the GDScript
|
||||
@@ -208,25 +208,15 @@ func _get_filled_snapshot_count() -> int:
|
||||
|
||||
|
||||
func start_capture() -> void:
|
||||
if _active:
|
||||
if is_open():
|
||||
return
|
||||
# Capture screenshot BEFORE showing the dialog overlay
|
||||
_captured_screenshot = get_viewport().get_texture().get_image()
|
||||
_active = true
|
||||
visible = true
|
||||
MetaStack.push(self)
|
||||
open()
|
||||
|
||||
# Pause the simulation
|
||||
(
|
||||
SimBridge
|
||||
. send_input(
|
||||
{
|
||||
"action": InputMapper.Action.PAUSE,
|
||||
"timestamp_msec": Time.get_ticks_msec(),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# Create the LineEdit dynamically
|
||||
func on_open() -> void:
|
||||
_line_edit = LineEdit.new()
|
||||
_line_edit.placeholder_text = "Describe the issue..."
|
||||
_line_edit.size = Vector2(BOX_WIDTH - PADDING * 2, 30)
|
||||
@@ -240,41 +230,23 @@ func start_capture() -> void:
|
||||
_line_edit.grab_focus()
|
||||
|
||||
|
||||
func _on_text_submitted(text: String) -> void:
|
||||
_save_report(text)
|
||||
_close()
|
||||
capture_completed.emit()
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not _active:
|
||||
return
|
||||
if event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
||||
_close()
|
||||
capture_cancelled.emit()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _close() -> void:
|
||||
_active = false
|
||||
visible = false
|
||||
func on_close() -> void:
|
||||
if _line_edit:
|
||||
_line_edit.release_focus()
|
||||
_line_edit.queue_free()
|
||||
_line_edit = null
|
||||
|
||||
_captured_screenshot = null
|
||||
|
||||
# Unpause the simulation
|
||||
(
|
||||
SimBridge
|
||||
. send_input(
|
||||
{
|
||||
"action": InputMapper.Action.UNPAUSE,
|
||||
"timestamp_msec": Time.get_ticks_msec(),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
func on_escape() -> bool:
|
||||
capture_cancelled.emit()
|
||||
return false # let MetaStack close
|
||||
|
||||
|
||||
func _on_text_submitted(text: String) -> void:
|
||||
_save_report(text)
|
||||
close()
|
||||
capture_completed.emit()
|
||||
|
||||
|
||||
func _save_report(description: String) -> void:
|
||||
@@ -456,7 +428,7 @@ func _render_snapshot_text() -> String:
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if not _active:
|
||||
if not is_open():
|
||||
return
|
||||
var viewport_size := get_viewport_rect().size
|
||||
# Full-screen dim
|
||||
@@ -489,4 +461,4 @@ func _draw() -> void:
|
||||
|
||||
|
||||
func is_active() -> bool:
|
||||
return _active
|
||||
return is_open()
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# gdlint:disable=max-file-lines
|
||||
class_name CharacterCreation
|
||||
extends Control
|
||||
extends MetaScreen
|
||||
## #705: Character creation screen.
|
||||
## Live 3D preview via SubViewport + 5-tab customisation panel (Body/Head/Hair/Clothing/Accessories).
|
||||
## Emits creation_confirmed(descriptor) on start, creation_cancelled on back.
|
||||
+15
-22
@@ -1,5 +1,5 @@
|
||||
class_name DebugConsole
|
||||
extends Control
|
||||
extends MetaScreen
|
||||
|
||||
## In-game debug console (#581). Tilde key (`) toggles open/closed.
|
||||
## Semi-transparent panel anchored to bottom ~40% of screen.
|
||||
@@ -23,7 +23,6 @@ const ERROR_COLOR := Color("#d45d5d")
|
||||
const INPUT_COLOR := Color("#e8c547")
|
||||
|
||||
var _enabled: bool = true
|
||||
var _open: bool = false
|
||||
var _log_lines: Array[String] = []
|
||||
var _panel: PanelContainer = null
|
||||
var _output_log: RichTextLabel = null
|
||||
@@ -33,6 +32,7 @@ var _history_idx: int = -1
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
pauses_sim = true
|
||||
_load_prefs()
|
||||
visible = false
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
@@ -107,11 +107,11 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
get_viewport().set_input_as_handled()
|
||||
_toggle()
|
||||
return
|
||||
if _open:
|
||||
if is_open():
|
||||
# Consume all keyboard events — prevent movement/action leaking through
|
||||
get_viewport().set_input_as_handled()
|
||||
if event.keycode == KEY_ESCAPE:
|
||||
_close()
|
||||
close()
|
||||
|
||||
|
||||
func _on_input_key(event: InputEvent) -> void:
|
||||
@@ -126,34 +126,26 @@ func _on_input_key(event: InputEvent) -> void:
|
||||
|
||||
|
||||
func _toggle() -> void:
|
||||
if _open:
|
||||
_close()
|
||||
if is_open():
|
||||
close()
|
||||
else:
|
||||
_open_console()
|
||||
MetaStack.push(self)
|
||||
open()
|
||||
|
||||
|
||||
func _open_console() -> void:
|
||||
_open = true
|
||||
visible = true
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
func on_open() -> void:
|
||||
_input_line.clear()
|
||||
_input_line.grab_focus()
|
||||
_history_idx = -1
|
||||
pause_requested.emit() # D-088: pause sim while typing debug commands
|
||||
|
||||
|
||||
func _close() -> void:
|
||||
_open = false
|
||||
visible = false
|
||||
func on_close() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_input_line.release_focus()
|
||||
unpause_requested.emit() # D-088: resume sim when console closes
|
||||
|
||||
|
||||
func is_open() -> bool:
|
||||
return _open
|
||||
|
||||
|
||||
# -- Command input --
|
||||
|
||||
|
||||
@@ -422,8 +414,9 @@ func append_response(response: Dictionary) -> void:
|
||||
var text: String = response.get("text", "")
|
||||
var color := SUCCESS_COLOR if success else ERROR_COLOR
|
||||
_append_text(text, color)
|
||||
if not _open and _enabled:
|
||||
_open_console()
|
||||
if not is_open() and _enabled:
|
||||
MetaStack.push(self)
|
||||
open()
|
||||
|
||||
|
||||
# -- Log rendering --
|
||||
@@ -492,8 +485,8 @@ func _history_down() -> void:
|
||||
|
||||
func set_enabled(enabled: bool) -> void:
|
||||
_enabled = enabled
|
||||
if not _enabled and _open:
|
||||
_close()
|
||||
if not _enabled and is_open():
|
||||
close()
|
||||
_save_prefs()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
extends Control
|
||||
extends MetaScreen
|
||||
## #257: Loading screen overlay — blocks input during save/load round-trip.
|
||||
## Shown when LOAD_GAME fires; hidden when save_result arrives (success or failure).
|
||||
## Full-screen, dark overlay with centered status text.
|
||||
@@ -15,8 +15,9 @@ var _version_label: Label = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
closable_by_escape = false
|
||||
visible = false
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_build_ui()
|
||||
|
||||
@@ -76,9 +77,10 @@ func _read_client_version() -> String:
|
||||
|
||||
|
||||
func show_loading() -> void:
|
||||
visible = true
|
||||
MetaStack.push(self)
|
||||
open()
|
||||
|
||||
|
||||
## Hide the loading overlay. success=false is reserved for future failure-state UI.
|
||||
func hide_loading(_success: bool = true) -> void:
|
||||
visible = false
|
||||
close()
|
||||
@@ -1,4 +1,4 @@
|
||||
extends Control
|
||||
extends MetaScreen
|
||||
## #258: Main menu — New Game / Continue / Load Game / Quit.
|
||||
## New Game: opens character creation screen, then starts game.
|
||||
## Continue: loads most recent save directory.
|
||||
@@ -1,4 +1,4 @@
|
||||
extends Control
|
||||
extends MetaScreen
|
||||
|
||||
## #528: Audio settings dialog — 5-bus volume sliders.
|
||||
## #646: AI-Enhanced Dialogue toggle + hardware detection status (D-138).
|
||||
@@ -6,7 +6,6 @@ extends Control
|
||||
## Volumes persist via AudioManager._save_prefs() on each slider change.
|
||||
## AI Dialogue toggle persists via ConfigFile (client-local) + ChangeSettings IPC (server SQLite).
|
||||
|
||||
signal closed
|
||||
signal debug_console_toggled(enabled: bool) # #581: debug console enabled/disabled
|
||||
signal ai_dialogue_toggled(enabled: bool) # #646: AI-Enhanced Dialogue enabled/disabled
|
||||
|
||||
@@ -37,7 +36,6 @@ const BUS_ROWS: Array = [
|
||||
["UI Sounds", "UISounds"],
|
||||
]
|
||||
|
||||
var _active: bool = false
|
||||
var _container: VBoxContainer = null
|
||||
|
||||
# #646: AI Dialogue hardware status and toggle node ref — used by testable API methods
|
||||
@@ -49,30 +47,17 @@ var _ai_battery_warning_label: Label = null # shown when on battery; toggle sta
|
||||
|
||||
func _ready() -> void:
|
||||
visible = false
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
|
||||
func open() -> void:
|
||||
if _active:
|
||||
return
|
||||
_active = true
|
||||
visible = true
|
||||
func on_open() -> void:
|
||||
_build_ui()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func close() -> void:
|
||||
if not _active:
|
||||
return
|
||||
_active = false
|
||||
visible = false
|
||||
func on_close() -> void:
|
||||
_destroy_ui()
|
||||
queue_redraw()
|
||||
closed.emit()
|
||||
|
||||
|
||||
func is_open() -> bool:
|
||||
return _active
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
@@ -139,7 +124,7 @@ func _build_ui() -> void:
|
||||
|
||||
var debug_check := CheckButton.new()
|
||||
# Query live DebugConsole node if available; fall back to prefs file
|
||||
var console_node := get_node_or_null("/root/Main/ModalLayer/DebugConsole")
|
||||
var console_node := get_node_or_null("/root/Main/MetaLayer/DebugConsole")
|
||||
if console_node and console_node.has_method("is_enabled"):
|
||||
debug_check.button_pressed = console_node.is_enabled()
|
||||
else:
|
||||
@@ -351,7 +336,7 @@ func _save_ai_pref(enabled: bool) -> void:
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if not _active:
|
||||
if not is_open():
|
||||
return
|
||||
var viewport_size := get_viewport_rect().size
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/settings_dialog.gd" id="1_settings"]
|
||||
[ext_resource type="Script" path="res://ui/meta/screens/settings/settings_dialog.gd" id="1_settings"]
|
||||
|
||||
; #528: Audio settings dialog — 5-bus volume sliders, OPEN_MENU (ESC) to toggle
|
||||
[node name="SettingsDialog" type="Control"]
|
||||
|
||||
Reference in New Issue
Block a user