Files
stickman/specs/phase_9_prop_editor_round6.md
T
ryan b66ded5f60 Add headless regression tests for Prop Editor functionality
- Implement tests for PropFile, including preset conversion, file dictionary conversion, round-trip serialization, version rejection, default values for missing keys, flattening, sanitization, and save/load functionality.
- Create tests for PropEditor, covering window title formatting, mode button text, safe saving, and recent file list management.
- Add tests for PropTheme, validating fallback behavior, invalid input handling, and default values for primitives and UI tokens.
- Introduce tests for ThumbnailCache, ensuring proper key formatting, PNG handling, and behavior with missing files.
2026-09-19 21:26:03 -04:00

3.2 KiB

Phase 9 Prop Editor — Round 6 (Recent-on-save, Reshaped Flag, Selection Contrast)

Status: implemented (2026-09-19; direct round — no open design choices).

Source: three user feedback points against the shipped Round 5 Prop Editor.

Feedback summary

  1. Saving a file should add it to File → Recent when it isn't there yet.
  2. A circle's inspector Radius field becomes meaningless once the circle's vertices are reshaped; every primitive needs a reshaped flag so primitive-specific inspector fields hide/disable after reshaping.
  3. A white shape in front of a white shape makes the white selection outline/box invisible; the selection color should contrast with the shape's fill.

Design decisions

  • Recent-on-save: _save_to() calls _push_recent(path) on success (dedupe + front-insert + trim, same helper as load — a save is recent activity).
  • reshaped flag: new boolean on every shape dict (internal + export + .prop schema, default false; additive and backward compatible — old files load as false). Set to true by: insert_vertex, remove_vertex, vertex-drag motion, create_point_on_edge (via insert), and non-uniform scaling of a circle (both the gizmo drag and scale_selected); cleared by set_shape_radius (which regenerates points from the radius). Translate/rotate/mirror/uniform-scale do not set it — they preserve primitive-ness. The inspector hides the Radius row and shows a muted "✏️ This shape was reshaped" note when the selected shape's flag is set (the flag exists on every primitive so future primitive-specific fields can gate on it the same way).
  • Selection contrast: new PropCanvas.contrast_color_for(fill_hex) -> Color — dark (#1F1F1F) for light fills (luminance > 0.5), white otherwise. Applied to the selection polyline and all gizmo drawing (bbox, rotation dot, scale crosses), which were previously hard white and vanished on white shapes. Vertex handles stay fill-colored (stickman-editor parity, Round 2 decision); the Create-Point flash stays yellow.

Changes

Target Change
scripts/prop_editor.gd _save_to pushes the path to recents on success; _reshaped_note label; _refresh_inspector_from_selection gates _radius_row.visible on not reshaped and shows the note when reshaped.
scripts/prop_canvas.gd "reshaped" in _make_shape_dict/_coerce_shape/_export_shape; mutation sites set/clear it; contrast_color_for static; selection polyline + gizmos use the contrast color.
scripts/prop_file.gd "reshaped" serialized/parsed (default false).
tests/test_prop_editor_canvas.gd New _test_reshaped_flag (insert/remove/radius-reset/scale/mirror/translate/rotate matrix) and _test_selection_contrast.
tests/test_prop_editor_file.gd Round-trip reshaped preservation + default-false assertions.

Verification

  • --check-only clean; all five suites green (file 68, theme 53, canvas 100, thumb 10, round3 21).
  • Manual F6: save a new prop → appears in File → 🕑 Recent; reshape a circle (drag a vertex, create a point, or stretch non-uniformly) → Radius row disappears + muted note; select a white shape → dark outline/box/dot/crosses.