Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ namespace Aspid.FastTools.Types.Editors

Список автоматически ограничивается подтипами класса, в котором объявлено поле. Дополнительная настройка не требуется.

Так как сменой типа управляет сам список, встроенная строка **Script** в Inspector скрывается, пока присутствует селектор — тип меняется только через выпадающий список (только UIToolkit-инспекторы; устаревший IMGUI-инспектор рисует эту строку сам).

```csharp
using UnityEngine;
using Aspid.FastTools.Types;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using Aspid.FastTools.Editors;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -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<AttachToPanelEvent>(_ => HideScriptField(field));

return field;
}

/// <summary>
/// Hides the inspector's default "Script" (<c>m_Script</c>) row for the component that owns
/// <paramref name="field"/>. UIToolkit-only: the IMGUI inspector draws that row itself, out of
/// reach of a field-level drawer, so <see cref="OnGUI"/> cannot suppress it.
/// </summary>
private static void HideScriptField(VisualElement field)
{
var inspector = field.GetFirstAncestorOfType<InspectorElement>();
if (inspector is null) return;

inspector.Query<PropertyField>()
.Where(propertyField => propertyField.bindingPath == "m_Script")
.ForEach(propertyField => propertyField.style.display = DisplayStyle.None);
}

/// <param name="property">Must own its <see cref="SerializedObject"/> (see <c>Persistent()</c>) —
/// it is read one <see cref="EditorApplication.delayCall"/> tick later.</param>
private static void ReplaceComponentScript(SerializedProperty property, Type oldType, Type newType)
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG_RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions README_RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ namespace Aspid.FastTools.Types.Editors

Список автоматически ограничивается подтипами класса, в котором объявлено поле. Дополнительная настройка не требуется.

Так как сменой типа управляет сам список, встроенная строка **Script** в Inspector скрывается, пока присутствует селектор — тип меняется только через выпадающий список (только UIToolkit-инспекторы; устаревший IMGUI-инспектор рисует эту строку сам).

```csharp
using UnityEngine;
using Aspid.FastTools.Types;
Expand Down
1 change: 1 addition & 0 deletions docs/QA-CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ability>` 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<T>` — picking it opens a second page to choose `T` and stores the closed `StackModifier<float>`; 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.
Expand Down
1 change: 1 addition & 0 deletions docs/QA-CHECKLIST_RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>` над `Ability` и его наследниками — включая абстрактную базу, `TypeAllow.All`; STEP 2 pickers элементов `[TypeSelector]` `string[]`, ограниченные `AbilityModifier`; STEP 3 picker предлагает открытый generic `StackModifier<T>` — при выборе открывается вторая страница для `T`, хранится закрытый `StackModifier<float>`; 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.
Expand Down
Loading