Skip to content

Add [TypeSelector] dropdown for [SerializeReference] and type repair#49

Open
VPDPersonal wants to merge 82 commits into
mainfrom
feature/serialize-reference-dropdown
Open

Add [TypeSelector] dropdown for [SerializeReference] and type repair#49
VPDPersonal wants to merge 82 commits into
mainfrom
feature/serialize-reference-dropdown

Conversation

@VPDPersonal

@VPDPersonal VPDPersonal commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Extends [TypeSelector] to [SerializeReference] and adds a full authoring + integrity toolkit for managed references. Every row works in both IMGUI and UIToolkit.

Area What it does
Picker on [SerializeReference] Hierarchical type dropdown on single / array / List<T> fields. Pick → instantiate, <None> → clear, the instance's nested props draw inline. Candidates default to the declared type, narrowed by [TypeSelector(typeof(IMelee))].
Open generics Offers Modifier<T>; arguments are inferred from a closed-generic field or picked in a follow-up window honouring constraints.
Editing Matching data preserved on type switch; Copy/Paste between compatible fields; multi-object edit applies one instance per object in a single Undo group.
Picker UX [TypeSelectorItem] (display name / Category/Name / tooltip / order / icon); per-project Favorites ★ + Recent; collapsible groups with in-header search.
Missing-type repair Reads the orphaned type straight from YAML (works around UUM-129100). Inline Fix re-points the ref and keeps its data — at any nesting depth, for saved assets, Prefab Mode and saved scenes. Smart Fix ranks the likeliest replacement for a one-click apply.
Aliased refs Make Unique action + deterministic per-rid colour stripes for shared instances.
Managed References window Tools → Aspid 🐍 → Managed References FastTools, four tabs: Home (overview + installable samples), Inspect Asset (whole graph from YAML — MISSING / SHARED badges, orphans, inline Fix, Open Source Prefab), Project Audit (project sweep grouped by broken type, Fix all with YAML diff preview, per-fix receipts with Undo, deep-links to Inspect) and Settings.
Editor theming Project-level theme override (AspidThemeSettings + Project Settings page) that re-skins every Aspid FastTools editor view; a reusable AspidWindowFooter (version + GitHub link) is pinned to the bottom of the window.
Project tooling Incremental usage index → Find Usages + sr: Quick Search; delete-script guard; proactive breakage notification; build/CI gate (SerializeReferenceCiGate.RunCheck); [SerializeReferenceRequired]; Project Settings page.
Authoring Drag a MonoScript onto a field; named templates; Link to Existing; picker-backed list +; Create New Script.
Sample / docs / tests SerializeReferences sample (weapons/effects, melee/ranged types, generic Modifier<T>, broken/shared demo prefabs) with a step-by-step tutorial (TypeSelectorTutorial scene + TUTORIAL.md EN/RU); README EN/RU + Documentation + CHANGELOG; first EditMode test assembly (Aspid.FastTools.Unity.Editor.Tests).

Notes for review

  • New code lives under Unity/Editor/Scripts/SerializeReferences/. The selector was extracted into a reusable view; TypeSelectorWindow is now a thin dropdown host (public Show API unchanged, gained optional filter / additionalTypes).
  • Trickiest spots: generic-argument resolution, YAML detect/rewrite (SerializeReferenceYamlEditor), at-any-depth repair through nested refs and [Serializable] containers, and the bulk rewrite (edits asset files directly — irreversible, hence the diff preview + confirmation).
  • Window merge: the old separate Repair Missing References and Managed References graph windows are now one workbench, and the Welcome view's overview is embedded as the Home tab (the main structural change since earlier revisions).
  • Shared editor UI: a new project-level theme override (AspidThemeSettings / AspidThemeSettingsProvider, applied across ~11 editor views) and a reusable AspidWindowFooter component pinned to the window bottom.
  • USS: migrated to BEM (aspid-fasttools-type-selector); stylesheet renamed to Aspid-FastTools-TypeSelector.uss (meta GUID preserved).
  • Also on this branch: the [TypeSelector] unification (Unify SerializeReference selector into [TypeSelector] with usage analyzer #50) and the Aspid.FastTools.Analyzers submodule — AFT0001 fix (Unity declares SerializeReference without the Attribute suffix), a widened AFT0003, plus new AFT0004 / AFT0005 diagnostics. The submodule pointer tracks the analyzer's feat/aft-usage-rules branch (to be merged into its main separately).

Linked issues

Closes #10

… drawer

Add a hierarchical type-selector dropdown for [SerializeReference] fields
(single, array and List<T>), reusing TypeSelectorWindow. Picking a concrete
type instantiates it, <None> clears the reference, the assigned instance's
nested properties are drawn inline under a foldout, and an unresolved stored
type is surfaced as a missing-type warning. Implemented for both IMGUI and
UIToolkit inspectors.

TypeSelectorWindow.Show gains an optional candidate-type filter (backward
compatible) used to exclude UnityEngine.Object, open generics, strings and
delegates.
Add a loadout-system sample exercising [SerializeReferenceSelector]: single
IWeapon field, List<IWeapon>, abstract StatusEffect base, and a nested
[SerializeReference] inside Railgun. Includes an IMGUILoadout + forcing editor
to demonstrate the IMGUI path, EN/RU README, and a package.json sample entry.
Add Loadout.prefab (UIToolkit) and IMGUILoadout.prefab (IMGUI) with pre-filled
managed references — single (Railgun + nested BurnEffect), List<IWeapon>
([Pistol, Shotgun]) and abstract-base (FreezeEffect / BurnEffect) — so the
sample can be inspected without building it by hand. Update the EN/RU sample
README "How to run" to drive the prefabs.
- Offer open generic definitions (e.g. Modifier<T>) as candidates: infer
  arguments from a closed-generic field, or resolve them in a new
  recursive, constraint-aware GenericArgumentSelectorWindow, validating
  the closed type against the field before assignment. Works in IMGUI and UIToolkit.
- Add an optional additionalTypes pass-through to TypeSelectorWindow /
  HierarchyBuilder / TypeInfo, and render generic names as Modifier<T>.
- Add a non-abstract Modifier<T> hierarchy (IModifier) with closed-generic
  subclasses, plus IModifier / Modifier<float> / List<IModifier> fields on
  Loadout and IMGUILoadout to exercise open-generic selection and the T picker.
- Document the generic flow in the sample README (EN/RU).
- Note open-generic support and the new additionalTypes parameter in the
  CHANGELOG, and add a generic bullet to the feature section of all four READMEs.
…he dropdown

- Resolve open generics inside TypeSelectorWindow as in-window argument pages (hierarchy, search, breadcrumb, live preview), removing the separate GenericArgumentSelectorWindow and its focus issues
- Extract generic resolution into GenericTypeResolver and add an argumentFilter parameter to TypeSelectorWindow.Show; the flow stays dormant unless open generics are present
- Format generic type names recursively so nested closed generics render fully (Modifier<Modifier<Int32>>)
- Resolve the generic type definition when locating a script so Open Script works for closed generics
@VPDPersonal VPDPersonal added type: feature New feature or capability status: work-in-progress Draft / not ready for review area: runtime Runtime / player code area: editor Editor-only code area: samples Sample projects labels Jun 7, 2026
VPDPersonal and others added 10 commits June 7, 2026 21:07
…eric argument selector

Open generic definitions reach the selector through the additionalTypes
path, which bypasses the name and CompilerGeneratedAttribute checks
applied to ordinary candidates. As a result anonymous types and closure
display classes (e.g. <>c__11<T>, <>f__AnonymousType0<...>) showed up as
argument candidates and bloated the unconstrained (object) list.

Exclude compiler-generated types in IsAssignableGenericDefinition so the
single gate for generic definitions filters them out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Carry the foldout caption on the toggle's aligned BaseField label so the
  type dropdown starts at the inspector value column, matching SerializableType
- Centre the header row so the open-script button lines up with the dropdown
- Split the dropdown into field/input nodes and cancel the EnumField caption's
  -2px left margin so the text indents like SerializableType
…/paste

- CreateInstancePreservingData carries matching [SerializeField] data across a
  type switch via JsonUtility ToJson/FromJsonOverwrite, instead of resetting
- SerializeReferenceClipboard backs a header right-click Copy/Paste menu scoped
  to the field (IMGUI ContextClick + UIToolkit ContextualMenuManipulator); paste
  builds an independent instance and is disabled when the type is not assignable
… references

- Add an Edit Type action to the missing-type warning: it opens an
  Assembly/Namespace/Class editor and rewrites the stored managed-reference
  type straight into the asset YAML (parser-free, no external deps), since
  Unity cannot reassign a missing reference through the serialization API.
  Applies to ScriptableObjects — the only assets where Unity preserves missing
  references (UUM-129100).
- Detect an aliased managed reference (two fields sharing one instance) and
  offer a Make Unique Reference action that clones it into an independent copy.
- Sample: add a WeaponPreset ScriptableObject with a pre-broken asset, plus
  shared-reference and missing-type demo prefabs, and document how to test both.
…YAML

- Replace the manual Edit-Type window with a Fix button that opens the existing
  hierarchical type picker; the chosen type is rewritten into the asset YAML.
- Read the missing type straight from the asset file (stored id + RefIds type)
  instead of Unity's serialization API, which reports nothing per-property and
  drops the reference on prefabs/GameObjects (UUM-129100). Repair now works on
  prefab assets too, not just ScriptableObjects.
- Detect strictly per-field by resolving the recorded type, removing the
  single-missing fallback that falsely flagged legitimately-empty fields.
- Scan a prefab/ScriptableObject's YAML for every orphaned managed reference —
  at any nesting depth and on any child object — by walking each document's flat
  RefIds block and flagging entries whose stored type no longer resolves.
- List each with its own Fix picker that rewrites the type in the file and
  reimports, reaching references the per-field drawer cannot (nested values,
  child-object components, anything Unity dropped to <None>) without Prefab Mode.
- Open via Tools → Aspid FastTools → Repair Missing References (auto-targets the
  current selection).
… Mode repair

Replace the bulky missing-type and shared-reference help boxes with a
compact yellow inline notice: a small warning icon, terse text and an
underlined, clickable action word (Fix / Make unique) with the full
detail on hover. Covers both the IMGUI and UIToolkit drawers.

Extend missing-type repair to objects open in Prefab Mode: detection
resolves the backing document through the prefab stage (matching the
asset's file id), and the fix is applied in memory — reassigning the
reference and recovering the orphaned field data — because rewriting the
open stage's file would be discarded on save. Saved assets keep the
YAML-rewrite path.

Reselect the inspected objects after a repair so Unity's cached
object-level missing-types banner clears, and guard the UIToolkit field
against the live SerializedObject being invalidated by a reimport.
…n containers

Generalise the YAML reference resolver behind the inline missing-type Fix to
walk a property path of any shape: through managed-reference chains and through
plain [Serializable] containers (struct/class fields and List<T> of them), so a
missing type buried in a slot or list element is detected and repaired inline
like a top-level field.

When an in-memory (Prefab Mode) repair replaces a missing reference that itself
carried missing nested references, clear those now-orphaned entries too so the
object's missing-types banner clears.

Add the SlottedLoadout sample demonstrating [SerializeReferenceSelector]
references inside a container field and a List<T> of containers.
- Redesign the Repair References window in the Welcome style: boxed
  asset card with an Aspid header, centred info/success hero states,
  warning-accented results header and amber gradient rows.
- Extract the hierarchical type selector from TypeSelectorWindow into a
  reusable TypeSelectorView; the window stays a thin dropdown host with
  an unchanged public Show API.
- Expand the selector inline as an accordion under the clicked Fix row
  (dark Aspid panel, one at a time, Escape collapses) instead of the
  floating grey dropdown.
- Migrate selector USS classes to BEM under the
  aspid-fasttools-type-selector block and rename the stylesheet to
  Aspid-FastTools-TypeSelector.uss; sync the asset field when Open()
  retargets an already-open window.
- The repair window moved to Tools → Aspid 🐍 → Repair Missing
  References FastTools; mirror the new path in the CHANGELOG, both
  root/Documentation README pairs and the sample READMEs.
@VPDPersonal VPDPersonal changed the title Add [SerializeReferenceSelector] dropdown for SerializeReference fields Add [SerializeReferenceSelector] dropdown and missing-type repair Jun 10, 2026
- Remove `[SerializeReferenceSelector]`; `[TypeSelector]` now also drives `[SerializeReference]` managed-reference fields, dispatching by property type in `TypeSelectorPropertyDrawer`.
- The attribute's base types narrow the candidate list below the declared field type — applied to both concrete types and open generic definitions, in the dropdown and the missing-type Fix picker.
- Update samples, CHANGELOG and READMEs (EN/RU) to the merged attribute.
- Add the `Aspid.FastTools.Analyzers` git submodule (VPDPersonal/Aspid.FastTools.Analyzers) carrying the `AFT*` [TypeSelector] usage rules.
- Ship the prebuilt `Aspid.FastTools.Analyzers.dll` with a `RoslynAnalyzer`-labelled meta (all platforms excluded), mirroring the generator DLL.
- Document the submodule and its manual rebuild/deploy in CLAUDE.md.
- Bump the analyzer submodule: AFT0003 now also flags a sealed class paired with an interface it does not implement; redeploy the prebuilt DLL.
- Switch the submodule URL from SSH to HTTPS so public clones resolve it without keys.
- Add the rebuild-analyzers-on-change PostToolUse hook script and the build-analyzer skill mirroring the generator pipeline; document both in CLAUDE.md.
…attribute

Unify SerializeReference selector into [TypeSelector] with usage analyzer
- Paint the accent gradient as one continuous mesh with shared boundary
  vertices instead of many separately-filled painter2D strips.
- Adjacent strips each anti-aliased their own edges to transparent, so the
  dark background bled through at every seam as faint vertical lines.
- A single mesh is only smoothed on its outer silhouette, removing the
  seams while keeping the quadratic alpha falloff.
- Replace the standalone Welcome window with a square "home" tab; the
  Welcome UI moves into a reusable WelcomeView and first-run auto-show now
  opens this window on the home tab.
- Add a rightmost square "settings" tab (placeholder SettingsView) sharing
  the --square / __tab-icon styling with the home tab.
- Generate outline home and gear icons under Resources/Icons, tinted per
  tab state: grey idle, brighter on hover, green when active.
- Rename the window title to "Aspid FastTools" and restore the multi-tone
  "traffic light" canvas gradient on the home tab.
@VPDPersonal VPDPersonal marked this pull request as ready for review June 19, 2026 07:19
@github-actions github-actions Bot added status: needs-review Ready for review and removed status: work-in-progress Draft / not ready for review labels Jun 19, 2026
- Add AspidThemeSettings, AspidThemeSettingsProvider and AspidThemeStyleSheetExtensions so editor components can layer a user theme override over the default dark palette
- Route internal editor elements through AddAspidThemeStyleSheets() instead of hard-wiring AspidStyles.DefaultStyleSheet (Id/Type fields, drawers, registry, selectors and the serialize-reference windows)
…scan

- Rebuild the Inspect Asset graph view as collapsible gradient-header cards with two-line nodes and an amber accent for broken refs, matching the Project Audit design
- Flatten the Project Audit Rescan button to a header-style action: drop its left padding and pill background so it lines up with the panel title and reads like the group "Fix all" headers
- Extend AspidGradientButton with trailing label and trailing-content support consumed by the redesigned cards
…ypes

- Add IMelee/IRanged interfaces and a Sword melee type so the TypeSelector demo shows constrained managed-reference picking
- Add a TypeSelectorTutorial scene and script, and update Pistol/Railgun/Shotgun to fit the expanded sample
- Add TUTORIAL.md / TUTORIAL_RU.md walkthrough (Lessons 1–8) keyed to the TypeSelectorTutorial scene and script
- Link the tutorial from the sample README/README_RU and update the package manifest sample description
- Render each sample's package.json description as a wrapped line under a full-width divider in the samples list
- Lay out the sample name and its Import/Reimport action on a single header row
- Replace the imported-state "Select" (ping folder) with "Reimport", which re-imports over the existing copy after a confirm dialog
- Mirror the gradient button's hover recolor for the relocated name/action labels
- Introduce AspidWindowFooter: a transparent bottom bar pairing the package version (linking to its GitHub release tag) with a GitHub link, above a faded divider
- Pin it to the bottom of the Managed References window so it persists across every tab/mode instead of living inside the Welcome tab
- Drop the now-redundant inline footer from the Welcome UXML
- Wrap long Project Audit entry paths onto multiple lines (min-width:0) so the rid stays pinned right instead of overflowing into a horizontal scroll
- Stack one summary help-box per bulk Fix all and keep the results region when the last group clears, so prior receipts survive until an explicit Rescan instead of being overwritten or dropped
- Skip Unity scenes in the object-loading scanners via a shared IsScene helper (LoadAllAssetsAtPath cannot read .unity files); fall back to the YAML pass
- Defer the ListView add-button install one tick to avoid mutating the subtree mid-attach
- Restyle the Inspect Asset reference graph (GraphView + ReferenceGraph.uss)
- Each fix receipt carries a right-pinned Undo button that re-points its entries back to the original missing type, restoring the broken state (only the YAML type line moves, data blocks stay on disk, so the revert is faithful)
- Undo drops only its own receipt and re-renders the group list, leaving receipts and Undo buttons for other still-applied fixes standing
- Extract the scene/Prefab-Mode skip filter and the batched per-file rewrite into shared helpers used by both the fix and the undo
- EditorWindow.minSize only bounds the floating window; a docked column
  ignores it and squeezes the content into character-wrapped, vertical text.
- Give the mode container a min-width so the content slides off-screen
  (clipped) instead of collapsing; toolbar/footer stay flexible.
- Position the per-receipt Undo chip absolute in the help box's top-right
  corner instead of in the row's flex flow, so the title and message
  stretch edge to edge instead of being cut short at the chip.
- Picking <None> in the Project Audit Fix all picker now nulls the
  group's broken references instead of being a no-op.
- On disk: rewrite each field/array pointer to the null id (-2) and add
  Unity's RefIds null sentinel, dropping the broken payload
  (TryNullReference) — matches what Unity writes for a null reference.
- Open in Prefab Mode / a loaded scene: null the reference on the live
  object in memory (TryClearMissingReferenceInMemory), saved with the
  asset, since a file rewrite there would be clobbered on save.
- Stop rendering the -2 null sentinel as a graph node so cleared fields
  no longer surface as "rid -2" shared cards.
- Render unassigned [SerializeReference] fields (null sentinel rid -2) as
  dim <None> leaves so a cleared or never-set slot stays visible in the
  graph instead of dropping out.
- Carry the full field path of each nested reference (e.g.
  _primaryWeapon._chargeEffect) via a new ReferenceGraphEdge.
- Add scanner structural tests and an empty-fields YAML fixture; show the
  empty slots in the Loadout sample prefab. Refresh READMEs and CHANGELOG.
- The tab labels "Inspect Asset" / "Project Audit" read as generic tools;
  rename to "Asset References" / "Project References" so they reflect the
  managed-reference scope the window already declares.
- Sync the labels across code comments, CHANGELOG and the sample docs.
- Keep a document whose RefIds block resolves to zero real nodes but
  still holds a field pointer, so an asset whose every [SerializeReference]
  field is unassigned renders its slots as <None> instead of dropping out
  with a "No managed references" empty state.
- Only return null now when there is neither a node nor a field pointer
  (every managed-ref field is an empty list — nothing to graph).
- Add an all-unassigned fixture and a regression test.
- Picking <None> in a missing card's Fix picker now nulls the reference
  instead of doing nothing — an empty type name fell through the null-type
  guard and silently no-op'd.
- Add a Clear action on every assigned reference card that resets it to
  <None>, so a reference can be cleared from the graph everywhere, not just
  re-pointed when missing.
- Both route through a shared ClearReference (YAML null-sentinel rewrite);
  confirmed and not undoable, with a neutral-tinted __clear-reference button.
- Document the new clear behavior in the EN/RU READMEs.
- Every reference card in the Managed References graph is now an inline
  type dropdown: picking a type assigns or re-points the reference and
  <None> clears it. Drops the dedicated Clear button on assigned cards —
  resetting is done through the picker, not a separate action.
- Healthy and empty (unassigned) slots edit through the live
  SerializedObject API keyed by field path, so Unity creates or removes the
  RefIds entry as the Inspector would; missing references keep the YAML
  rewrite (Unity cannot reassign a missing type through the API).
- Persist live edits via SavePrefabAsset for prefab assets and
  SaveAssetIfDirty for ScriptableObjects, then rescan from disk.
- Neutral-tint the dropdown band; the amber accent moves to a --missing
  modifier so only a broken card reads as a call to action.
- Healthy and empty graph cards now label their dropdown band ("Change ▼" /
  "Assign ▼") instead of showing a lone chevron, matching the missing
  card's "Fix Missing ▼".
- The empty (<None>) card drops its dimmer fill/border override so it reads
  at the same weight as the live cards — it is now an editable dropdown, so
  only its dim italic <None> placeholder sets it apart.
Nesting is read from each card's field path (e.g. _primaryWeapon._chargeEffect),
so the per-depth left margin was redundant. Render every card flush in a flat
column; remove the depth parameter threaded through AppendNode / BuildNodeCard /
BuildEmptySlotCard / BuildBackEdgeCard and the IndentStep constant.
- The Asset References "Rescan" row sized to its field (height:auto, ~24px)
  while the Project References "Scan Project" button kept the gradient
  button's 30px default, so the two tabs' top panels differed in height. Pin
  the Rescan row to 30px so both action rows — and the panels — match.
- Fix two invalid unitless USS lengths in the same file: padding-left: 4 and
  margin-bottom: 2 (now 4px / 2px).
NestedLoadout.prefab is a three-level hierarchy (NestedLoadout → WeaponSlot
→ BackupSlot) with a Loadout on every object, each carrying a missing
[SerializeReference] type (GhostPistol / GhostBlade / GhostAura) the
Inspector can't reach outside Prefab Mode — plus a nested reference, an
empty <None> slot and healthy refs. Exercises the graph's per-component
multi-document view, missing types on child objects, and the inline
assign / re-point / clear dropdowns. Documented in the sample README EN/RU.
The per-document header bands floated transparent over the dotted canvas,
their fill showing only on hover. Give them the same resting card surface
as the node cards below (translucent dark fill + hairline border) so each
header reads at the same weight as its group; the amber issue hover glow is
kept.
- Bind Alt+1/2/3/0 to Home/Asset/Project/Settings via ShortcutManager with
  the window as context, so the keys work whenever the window is focused
  (a panel KeyDownEvent only fired while an inner element held focus)
- Show the live binding as a keyboard-cap badge on the two mode tabs and as
  a tooltip on every tab, reading it back from ShortcutManager so it tracks
  user rebinds in Edit > Shortcuts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: editor Editor-only code area: runtime Runtime / player code area: samples Sample projects status: needs-review Ready for review type: feature New feature or capability

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

SerializeReferenceSelector

1 participant