diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/EN/README.md b/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/EN/README.md index 05b79688..362323d9 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/EN/README.md +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/EN/README.md @@ -340,6 +340,8 @@ A serializable struct that renders a type-switching dropdown in the Inspector. A The dropdown is automatically constrained to subtypes of the class that declares the field. No additional configuration is required. +Because the dropdown owns type-switching, the Inspector's built-in **Script** row is hidden while the selector is present — you change the type only through the dropdown (UIToolkit inspectors only; the legacy IMGUI inspector draws that row itself). + ```csharp using UnityEngine; using Aspid.FastTools.Types; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/RU/README.md b/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/RU/README.md index 875981ae..05590274 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/RU/README.md +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Documentation/RU/README.md @@ -340,6 +340,8 @@ namespace Aspid.FastTools.Types.Editors Список автоматически ограничивается подтипами класса, в котором объявлено поле. Дополнительная настройка не требуется. +Так как сменой типа управляет сам список, встроенная строка **Script** в Inspector скрывается, пока присутствует селектор — тип меняется только через выпадающий список (только UIToolkit-инспекторы; устаревший IMGUI-инспектор рисует эту строку сам). + ```csharp using UnityEngine; using Aspid.FastTools.Types; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Unity/Editor/Scripts/Types/Drawers/ComponentTypeSelectorPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Unity/Editor/Scripts/Types/Drawers/ComponentTypeSelectorPropertyDrawer.cs index a5d2368c..2e841e77 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Unity/Editor/Scripts/Types/Drawers/ComponentTypeSelectorPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Unity/Editor/Scripts/Types/Drawers/ComponentTypeSelectorPropertyDrawer.cs @@ -2,6 +2,7 @@ using UnityEngine; using UnityEditor; using UnityEngine.UIElements; +using UnityEditor.UIElements; using Aspid.FastTools.Editors; // ReSharper disable once CheckNamespace @@ -56,9 +57,29 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) field.RegisterValueChangedCallback(evt => ReplaceComponentScript(persistent, currentType, evt.newValue)); + // The built-in "Script" row is redundant here: the dropdown above owns type-swapping + // by rewriting m_Script, so let the user edit the type only through it. Hide the sibling + // m_Script field once this drawer joins the inspector's visual tree. + field.RegisterCallback(_ => HideScriptField(field)); + return field; } + /// + /// Hides the inspector's default "Script" (m_Script) row for the component that owns + /// . UIToolkit-only: the IMGUI inspector draws that row itself, out of + /// reach of a field-level drawer, so cannot suppress it. + /// + private static void HideScriptField(VisualElement field) + { + var inspector = field.GetFirstAncestorOfType(); + if (inspector is null) return; + + inspector.Query() + .Where(propertyField => propertyField.bindingPath == "m_Script") + .ForEach(propertyField => propertyField.style.display = DisplayStyle.None); + } + /// Must own its (see Persistent()) — /// it is read one tick later. private static void ReplaceComponentScript(SerializedProperty property, Type oldType, Type newType) diff --git a/CHANGELOG.md b/CHANGELOG.md index 326027d7..d29b112b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Shipped in the prebuilt `Aspid.FastTools.Analyzers` Roslyn DLL: - The per-property missing-type probe now caches the asset YAML by path + write-time, eliminating a `File.ReadAllLines` on every IMGUI repaint. - The confirm dialog for clearing a missing managed reference now names how many fields it will null, so an aliased reference shared across several slots makes its all-pointer clear explicit before the irreversible YAML edit (the clear still nulls every aliased field — only the wording changed). - `[TypeSelector].Allow` now defaults to `TypeAllow.All` instead of `TypeAllow.None`: a type-name field (`string` / `string[]` / `SerializableType`) now lists abstract classes and interfaces in the picker by default — pass `Allow = TypeAllow.None` to restrict it to concrete types. This aligns the raw-`string` picker with `SerializableType`, which already offered them. A `[SerializeReference]` managed reference is unaffected: that path never offered abstract/interface types and ignores `Allow` entirely. +- `ComponentTypeSelector` now hides the Inspector's built-in **Script** row while the selector is present — type-switching goes through its dropdown, so the redundant row (which let you swap the script by hand and bypass the selector) is removed. UIToolkit inspectors only; the legacy IMGUI inspector draws that row itself and is unaffected. ### Fixed - A `null` element in a `Type[]` member referenced by a `string` / `SerializableType` `[TypeSelector]` field no longer throws `NullReferenceException` and aborts the type picker — null entries are filtered out before building the candidate list. ([#51]) diff --git a/CHANGELOG_RU.md b/CHANGELOG_RU.md index d3e4e9cc..d1e9752b 100644 --- a/CHANGELOG_RU.md +++ b/CHANGELOG_RU.md @@ -94,6 +94,7 @@ - Пер-свойственная проба missing-типа теперь кэширует YAML ассета по пути + времени записи, устраняя `File.ReadAllLines` на каждый IMGUI-repaint. - Диалог подтверждения очистки потерянной managed-ссылки теперь называет, сколько полей он обнулит, — алиасная ссылка, разделённая между несколькими слотами, делает свой сброс всех указателей явным перед необратимой правкой YAML (очистка по-прежнему обнуляет все алиасные поля — изменилась только формулировка). - `[TypeSelector].Allow` теперь по умолчанию `TypeAllow.All` вместо `TypeAllow.None`: type-name-поле (`string` / `string[]` / `SerializableType`) теперь по умолчанию показывает в пикере абстрактные классы и интерфейсы — передайте `Allow = TypeAllow.None`, чтобы ограничить его конкретными типами. Это выравнивает пикер сырого `string` с `SerializableType`, который их уже предлагал. Managed-ссылка `[SerializeReference]` не затронута: этот путь никогда не предлагал абстрактные/интерфейсные типы и полностью игнорирует `Allow`. +- `ComponentTypeSelector` теперь скрывает встроенную строку **Script** в Inspector, пока присутствует селектор — сменой типа управляет его дропдаун, поэтому избыточная строка (позволявшая менять скрипт вручную в обход селектора) убрана. Только UIToolkit-инспекторы; устаревший IMGUI-инспектор рисует эту строку сам и не затронут. ### Исправлено - `null`-элемент в `Type[]`-члене, на который ссылается `string` / `SerializableType` `[TypeSelector]`-поле, больше не кидает `NullReferenceException` и не обрывает пикер типов — null-записи отфильтровываются до построения списка кандидатов. ([#51]) diff --git a/README.md b/README.md index a2bc2571..b4d01e25 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,8 @@ A serializable struct that renders a type-switching dropdown in the Inspector. A The dropdown is automatically constrained to subtypes of the class that declares the field. No additional configuration is required. +Because the dropdown owns type-switching, the Inspector's built-in **Script** row is hidden while the selector is present — you change the type only through the dropdown (UIToolkit inspectors only; the legacy IMGUI inspector draws that row itself). + ```csharp using UnityEngine; using Aspid.FastTools.Types; diff --git a/README_RU.md b/README_RU.md index 11296740..feee89f8 100644 --- a/README_RU.md +++ b/README_RU.md @@ -343,6 +343,8 @@ namespace Aspid.FastTools.Types.Editors Список автоматически ограничивается подтипами класса, в котором объявлено поле. Дополнительная настройка не требуется. +Так как сменой типа управляет сам список, встроенная строка **Script** в Inspector скрывается, пока присутствует селектор — тип меняется только через выпадающий список (только UIToolkit-инспекторы; устаревший IMGUI-инспектор рисует эту строку сам). + ```csharp using UnityEngine; using Aspid.FastTools.Types; diff --git a/docs/QA-CHECKLIST.md b/docs/QA-CHECKLIST.md index ec7d4592..1acf4db9 100644 --- a/docs/QA-CHECKLIST.md +++ b/docs/QA-CHECKLIST.md @@ -128,6 +128,7 @@ - [ ] **ProfilerMarkers sample**: imports via Package Manager without errors; the `ProfilerMarkersTutorial` scene opens and the TUTORIAL steps are walkable in Play Mode — the `ProfilerMarkersTutorial.*` markers appear in the Profiler: named `Physics`/`Render`, auto-named `SimulateInput`, nested `AI → AI.Agent`, and two line-distinguished `SimulateAudio` entries; the `ProfilerMarkers` demo scene emits the same markers on Play. - [ ] **VisualElements sample**: imports via Package Manager without errors; the `VisualElementsTutorial` scene opens and the five STEP cards render (no Play Mode) — fluent-style swatch, font presets (normal/bold/italic/letter-spaced), the layout header row fed by Ability Name, the reactive Mana Cost badge flipping to **FREE** at `0`, and the STEP 5 ProgressBar/HelpBox/Button reacting to Charge (bar fills, HelpBox shows at 100%, **Log charge** prints); the `AbilityConfig` demo inspector still shows its card and FREE/warning badge. - [ ] **Types sample**: imports via Package Manager without errors; the `TypesTutorial` scene opens and the TUTORIAL steps are walkable (STEP 1 `SerializableType` picker over `Ability` and its subtypes — abstract base included, `TypeAllow.All`; STEP 2 `[TypeSelector]` `string[]` element pickers constrained to `AbilityModifier`; STEP 3 the picker offers the open generic `StackModifier` — picking it opens a second page to choose `T` and stores the closed `StackModifier`; STEP 4 `[TypeSelector(Required = true)]` empty string shows the inline "Required type is not set" notice and feeds the required-field gate; STEP 5 the Enemy's `ComponentTypeSelector` dropdown swaps `FastEnemy`↔`TankEnemy` in place with `Health` persisting; `Log Tutorial Lookups` logs the resolved ability/generic/required/enemy types); the `Types` demo scene logs the activated ability + modifiers on Play and swaps the Enemy script from the Inspector dropdown. *(IMGUI path: `Assets/DevTests/Types/Prefabs/TypesDevTest.prefab` in the dev project — forced-IMGUI `SerializableType`/`[TypeSelector]` inspectors next to their UIToolkit twin, plus a forced-IMGUI `ComponentTypeSelector` enemy, not shipped with the package.)* +- [ ] **ComponentTypeSelector — hidden Script row**: on a component / ScriptableObject with a `ComponentTypeSelector` field, the Inspector's built-in **Script** row is hidden in the UIToolkit inspector (type-switching only via the dropdown); it re-hides after an Undo / domain reload; sibling components in the same Inspector still show their own Script row. *(IMGUI path — the forced-IMGUI enemy in `TypesDevTest.prefab` — still shows the Script row by design.)* - [ ] **SerializedProperty Extensions**: `.SetValue()`, `.Apply()`, chained calls, reflection helpers. - [ ] **VisualElement Extensions**: spot-check the fluent API (layout/style/borders/callbacks/USS/child) + the Math satellite (`float2/3/4` INotifyValueChanged) with Mathematics installed. - [ ] **IMGUI Scopes**: Vertical/Horizontal/ScrollView — Rect properties, correct Dispose. diff --git a/docs/QA-CHECKLIST_RU.md b/docs/QA-CHECKLIST_RU.md index 03ed7eb2..cbef9429 100644 --- a/docs/QA-CHECKLIST_RU.md +++ b/docs/QA-CHECKLIST_RU.md @@ -128,6 +128,7 @@ - [ ] **Сэмпл ProfilerMarkers**: импорт через Package Manager без ошибок; сцена `ProfilerMarkersTutorial` открывается, TUTORIAL-шаги проходимы в Play Mode — маркеры `ProfilerMarkersTutorial.*` видны в Profiler'е: именованные `Physics`/`Render`, автоименованный `SimulateInput`, вложенный `AI → AI.Agent`, и две записи `SimulateAudio`, различаемые по строке; демо-сцена `ProfilerMarkers` даёт те же маркеры по Play. - [ ] **Сэмпл VisualElements**: импорт через Package Manager без ошибок; сцена `VisualElementsTutorial` открывается, пять карточек STEP рендерятся (без Play Mode) — fluent-образец стиля, пресеты шрифта (обычный/жирный/курсив/разрядка), строка-шапка из Ability Name, реактивный бейдж Mana Cost, переключающийся на **FREE** при `0`, и STEP 5 ProgressBar/HelpBox/Button, реагирующие на Charge (полоса заполняется, HelpBox появляется на 100%, **Log charge** выводит значение); демо-инспектор `AbilityConfig` по-прежнему показывает карточку и бейдж FREE/warning. - [ ] **Сэмпл Types**: импорт через Package Manager без ошибок; сцена `TypesTutorial` открывается, шаги TUTORIAL проходимы (STEP 1 picker `SerializableType` над `Ability` и его наследниками — включая абстрактную базу, `TypeAllow.All`; STEP 2 pickers элементов `[TypeSelector]` `string[]`, ограниченные `AbilityModifier`; STEP 3 picker предлагает открытый generic `StackModifier` — при выборе открывается вторая страница для `T`, хранится закрытый `StackModifier`; STEP 4 пустая строка `[TypeSelector(Required = true)]` показывает инлайн-notice «Required type is not set» и питает гейт обязательных полей; STEP 5 дропдаун `ComponentTypeSelector` у Enemy меняет `FastEnemy`↔`TankEnemy` по месту с сохранением `Health`; `Log Tutorial Lookups` логирует резолвнутые типы ability/generic/required/enemy); демо-сцена `Types` на Play логирует активированную способность + модификаторы и меняет скрипт Enemy из дропдауна Inspector. *(IMGUI-путь: `Assets/DevTests/Types/Prefabs/TypesDevTest.prefab` в dev-проекте — принудительно-IMGUI инспекторы `SerializableType`/`[TypeSelector]` рядом с их UIToolkit-двойником плюс принудительно-IMGUI враг `ComponentTypeSelector`, не поставляется с пакетом.)* +- [ ] **ComponentTypeSelector — скрытая строка Script**: на компоненте / ScriptableObject с полем `ComponentTypeSelector` встроенная строка **Script** скрыта в UIToolkit-инспекторе (смена типа только через дропдаун); повторно скрывается после Undo / перезагрузки домена; соседние компоненты в том же Inspector по-прежнему показывают свою строку Script. *(IMGUI-путь — принудительно-IMGUI враг в `TypesDevTest.prefab` — по-прежнему показывает строку Script by design.)* - [ ] **SerializedProperty Extensions**: `.SetValue()`, `.Apply()`, chain-вызовы, reflection-хелперы. - [ ] **VisualElement Extensions**: выборочная проверка fluent API (layout/style/borders/callbacks/USS/child) + Math-satellite (`float2/3/4` INotifyValueChanged) при установленном Mathematics. - [ ] **IMGUI Scopes**: Vertical/Horizontal/ScrollView — Rect-свойства, корректный Dispose.