Add Stage Surface Color Dialog and Background Ground Functionality

- Implemented StageSurfaceColorDialog for custom color selection with live preview.
- Created StageBackgroundGround class for non-walkable scenery behind the set.
- Introduced StageRectSurface for shared rectangular geometry and tint preview.
- Added StageSurfacePreview for rendering surface previews.
- Developed comprehensive tests for surface color selection, rendering, and serialization.
This commit is contained in:
2026-10-17 21:00:03 -04:00
parent a0510e6405
commit 26dd9c64e1
39 changed files with 3118 additions and 142 deletions
+9 -4
View File
@@ -44,7 +44,7 @@ func _run() -> void:
root.add_child(_stage)
await process_frame
await process_frame
_test_grid_controls_layout()
await _test_grid_controls_layout()
var baseline_spawner: StageSpawner = _stage.get("_spawner") as StageSpawner
var baseline_ground: TerrainBlock = baseline_spawner.spawn_terrain_asset("ground", Vector2.ZERO)
_expect(baseline_ground != null, "baseline navigation probe places one ordinary Ground block")
@@ -203,7 +203,7 @@ func _run() -> void:
for preview_node: Node in card_content.get_children():
if preview_node.get_script() == BACKGROUND_CARD_PREVIEW:
background_preview_count += 1
_expect(background_grid != null and background_grid.get_child_count() == 7 and background_preview_count == 7, "Sky and wall-finish cards render a distinct production-style preview")
_expect(background_grid != null and background_grid.get_child_count() == 6 and background_preview_count == 6, "four Sky looks and both surface setup cards render production-style previews")
var cloudy_status: Label = cloudy_card.find_child("StatusLabel", true, false) as Label if cloudy_card != null else null
_expect(cloudy_card != null and cloudy_card.button_pressed and cloudy_status != null and cloudy_status.text == "Current Sky", "gallery marks the loaded Town Street Sky as Current Sky")
var clear_sky_card: AssetCard = _find_card_by_id(background_grid, "sky_clear_day")
@@ -407,8 +407,13 @@ func _test_grid_controls_layout() -> void:
_expect(control_rect.position.y >= 0.0 and control_rect.end.y <= 64.0, "%s fits within the Stage top bar" % control.name)
var file_menu: MenuButton = chrome.call("get_file_menu") as MenuButton
if file_menu != null:
await _send_stage_click(file_menu.get_global_rect().get_center(), MOUSE_BUTTON_LEFT)
_expect(file_menu.get_popup().visible, "File menu opens when clicked beside the right-aligned Grid and Snap controls")
var file_menu_rect: Rect2 = file_menu.get_global_rect()
_expect(file_menu.is_visible_in_tree() and file_menu_rect.position.x >= 0.0 and file_menu_rect.end.x <= root.size.x, "File menu remains visible beside the right-aligned Grid and Snap controls")
# Headless SceneTree mouse injection reaches Stage canvas input but does not
# synthesize a Control pointer position, so verify the real MenuButton popup.
file_menu.show_popup()
await process_frame
_expect(file_menu.get_popup().visible, "File menu popup opens through its production MenuButton control")
file_menu.get_popup().hide()